Merge branch 'refs/heads/7311-add-peertask-foundation-code' into 7311-add-GetReceiptsFromPeerTask

# Conflicts:
#	ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/peertask/PeerTaskExecutor.java
#	ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/peertask/PeerTaskExecutorTest.java
pull/7638/head
Matilda Clerke 2 months ago
commit fae39a8db7
  1. 2
      .github/workflows/BesuContainerVerify.sh
  2. 109
      .github/workflows/docker-promote.yml
  3. 398
      .github/workflows/draft-release.yml
  4. 316
      .github/workflows/release.yml
  5. 2
      CHANGELOG.md
  6. 9
      MAINTAINERS.md
  7. 7
      acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/BesuNode.java
  8. 7
      acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ProcessBesuNodeRunner.java
  9. 15
      acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ThreadBesuNodeRunner.java
  10. 7
      acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeConfiguration.java
  11. 8
      acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeConfigurationBuilder.java
  12. 1
      acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeFactory.java
  13. 305
      besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java
  14. 3
      besu/src/main/java/org/hyperledger/besu/cli/options/DataStorageOptions.java
  15. 1
      besu/src/main/java/org/hyperledger/besu/cli/options/stable/ApiConfigurationOptions.java
  16. 36
      besu/src/main/java/org/hyperledger/besu/cli/options/stable/EngineRPCConfiguration.java
  17. 81
      besu/src/main/java/org/hyperledger/besu/cli/options/stable/EngineRPCOptions.java
  18. 1
      besu/src/main/java/org/hyperledger/besu/cli/options/stable/GraphQlOptions.java
  19. 34
      besu/src/main/java/org/hyperledger/besu/cli/options/stable/JsonRpcHttpOptions.java
  20. 1
      besu/src/main/java/org/hyperledger/besu/cli/options/stable/MetricsOptionGroup.java
  21. 238
      besu/src/main/java/org/hyperledger/besu/cli/options/stable/P2PDiscoveryOptions.java
  22. 1
      besu/src/main/java/org/hyperledger/besu/cli/options/stable/PermissionsOptions.java
  23. 1
      besu/src/main/java/org/hyperledger/besu/cli/options/unstable/MetricsCLIOptions.java
  24. 2
      besu/src/main/java/org/hyperledger/besu/cli/options/unstable/P2PTLSConfigOptions.java
  25. 4
      besu/src/main/java/org/hyperledger/besu/cli/subcommands/storage/TrieLogHelper.java
  26. 16
      besu/src/main/java/org/hyperledger/besu/components/BesuCommandModule.java
  27. 9
      besu/src/main/java/org/hyperledger/besu/services/BlockchainServiceImpl.java
  28. 2
      besu/src/test/java/org/hyperledger/besu/cli/CommandTestAbstract.java
  29. 1
      besu/src/test/java/org/hyperledger/besu/cli/options/stable/DataStorageOptionsTest.java
  30. 2
      build.gradle
  31. 2
      config/src/main/java/org/hyperledger/besu/config/MergeConfigOptions.java
  32. 18
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/TransactionAdapter.java
  33. 3
      ethereum/api/src/main/resources/schema.graphqls
  34. 47
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulator.java
  35. 84
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulatorTest.java
  36. 17
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/EthPeers.java
  37. 102
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/peertask/DefaultPeerSelector.java
  38. 30
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/peertask/PeerSelector.java
  39. 16
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/peertask/PeerTask.java
  40. 15
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/peertask/PeerTaskExecutor.java
  41. 10
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/request/StorageRangeDataRequest.java
  42. 135
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/peertask/DefaultPeerSelectorTest.java
  43. 20
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/peertask/PeerTaskExecutorTest.java
  44. 105
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/AccountHealingTrackingTest.java
  45. 39
      ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/discovery/P2PDiscoveryConfiguration.java
  46. 9
      ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/discovery/dns/DNSDaemonTest.java
  47. 6
      evm/src/main/java/org/hyperledger/besu/evm/operation/ReturnDataLoadOperation.java
  48. 2
      plugin-api/build.gradle
  49. 9
      plugin-api/src/main/java/org/hyperledger/besu/plugin/services/BlockchainService.java

@ -57,7 +57,7 @@ else
fi fi
# For the latest tag check the version match # For the latest tag check the version match
if [[ ${TAG} == "latest" && ${CHECK_LATEST} == "true" ]] if [[ ${TAG} =~ ^latest && ${CHECK_LATEST} == "true" ]]
then then
_VERSION_IN_LOG=$(docker logs ${CONTAINER_NAME} | grep "#" | grep "Besu version" | cut -d " " -f 4 | sed 's/\s//g') _VERSION_IN_LOG=$(docker logs ${CONTAINER_NAME} | grep "#" | grep "Besu version" | cut -d " " -f 4 | sed 's/\s//g')
echo "Extracted version from logs [$_VERSION_IN_LOG]" echo "Extracted version from logs [$_VERSION_IN_LOG]"

@ -0,0 +1,109 @@
name: Docker Promote
run-name: "Docker Promote ${{ github.event.release.name }}"
on:
release:
types: [released]
env:
registry: docker.io
GRADLE_OPTS: "-Dorg.gradle.parallel=true -Dorg.gradle.caching=true"
jobs:
validate:
runs-on: ubuntu-22.04
env:
RELEASE_VERSION: "${{ github.event.release.name }}"
steps:
- name: Pre-process Release Name
id: pre_process_release_version
run: |
# strip all whitespace
RELEASE_VERSION="${RELEASE_VERSION//[[:space:]]/}"
if [[ ! "$RELEASE_VERSION" =~ ^[0-9]+\.[0-9]+(\.[0-9]+)?(-.*)?$ ]]; then
echo "Release name does not conform to a valid besu release format YY.M.v[-suffix], e.g. 24.8.0-RC1."
exit 1
fi
echo "release_version=$RELEASE_VERSION" >> $GITHUB_OUTPUT # Set as output using the new syntax
outputs:
release_version: ${{ steps.pre_process_release_version.outputs.release_version }}
docker-promote:
needs: [validate]
env:
RELEASE_VERSION: ${{ needs.validate.outputs.release_version }}
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
- name: Setup Java
uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93
with:
distribution: temurin
java-version: 21
cache: gradle
- name: Login to ${{ env.registry }}
uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d
with:
registry: ${{ env.registry }}
username: ${{ secrets.DOCKER_USER_RW }}
password: ${{ secrets.DOCKER_PASSWORD_RW }}
- name: Setup Gradle
uses: gradle/actions/setup-gradle@9e899d11ad247ec76be7a60bc1cf9d3abbb9e7f1
with:
cache-disabled: true
- name: Docker upload
run: ./gradlew "-Prelease.releaseVersion=${{ env.RELEASE_VERSION }}" "-PdockerOrgName=${{ env.registry }}/${{ secrets.DOCKER_ORG }}" dockerUploadRelease
- name: Docker manifest
run: ./gradlew "-Prelease.releaseVersion=${{ env.RELEASE_VERSION }}" "-PdockerOrgName=${{ env.registry }}/${{ secrets.DOCKER_ORG }}" manifestDockerRelease
docker-verify:
needs: [validate,docker-promote]
env:
CONTAINER_NAME: besu-check
RELEASE_VERSION: ${{ needs.validate.outputs.release_version }}
runs-on: ${{ matrix.combination.runner }}
timeout-minutes: 4
strategy:
matrix:
combination:
- tag: latest-amd64
platform: 'linux/amd64'
runner: ubuntu-22.04
- tag: latest
platform: ''
runner: ubuntu-22.04
- tag: latest-arm64
platform: ''
runner: besu-arm64
- tag: latest
platform: ''
runner: besu-arm64
steps:
- name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
with:
sparse-checkout: '.github/workflows/BesuContainerVerify.sh'
- name: Start container
run: |
PLATFORM_OPT=""
[[ x${{ matrix.combination.platform }} != 'x' ]] && PLATFORM_OPT="--platform ${{ matrix.combination.platform }}"
docker run -d $PLATFORM_OPT --name ${{ env.CONTAINER_NAME }} ${{ secrets.DOCKER_ORG }}/besu:${{ matrix.combination.tag }}
- name: Verify besu container
run: bash .github/workflows/BesuContainerVerify.sh
env:
TAG: ${{ matrix.combination.tag }}
VERSION: ${{ env.RELEASE_VERSION }}
CHECK_LATEST: true
- name: Stop container
run: docker stop ${{ env.CONTAINER_NAME }}

@ -0,0 +1,398 @@
name: Draft Release
run-name: "Draft Release ${{ inputs.tag }}"
on:
workflow_dispatch:
inputs:
tag:
required: true
env:
registry: docker.io
GRADLE_OPTS: "-Dorg.gradle.parallel=true -Dorg.gradle.caching=true"
jobs:
validate:
runs-on: ubuntu-22.04
env:
RELEASE_VERSION: "${{ inputs.tag }}"
steps:
- name: Check default branch
run: |
echo "Current Branch: ${{ github.ref_name }}"
echo "Default Branch: ${{ github.event.repository.default_branch }}"
if [[ ${{ github.ref_name }} != ${{ github.event.repository.default_branch }} ]]
then
echo "This workflow can only be run on default branch. This is not an issue for hot fixes as code is checked out from the tag"
exit 1
fi
- name: Pre-process Release Name
id: validate_release_version
run: |
# strip all whitespace
RELEASE_VERSION="${RELEASE_VERSION//[[:space:]]/}"
if [[ ! "$RELEASE_VERSION" =~ ^[0-9]+\.[0-9]+(\.[0-9]+)?(-.*)?$ ]]; then
echo "Release name does not conform to a valid besu release format YY.M.v[-suffix], e.g. 24.8.0-RC1."
exit 1
fi
echo "release_version=$RELEASE_VERSION" >> $GITHUB_OUTPUT # Set as output using the new syntax
# Perform a tag checkout to ensure tag is available
- name: Verify tag Exist
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
with:
ref: ${{ steps.validate_release_version.outputs.release_version }}
fetch-depth: 1
outputs:
release_version: ${{ steps.validate_release_version.outputs.release_version }}
build:
runs-on: ubuntu-22.04
needs: validate
env:
RELEASE_VERSION: ${{ needs.validate.outputs.release_version }} # Use the output from the pre_process_release job
outputs:
tarSha: ${{steps.hashes.outputs.tarSha}}
zipSha: ${{steps.hashes.outputs.zipSha}}
steps:
- name: Checkout tag
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
with:
ref: ${{ env.RELEASE_VERSION }}
- name: Set up Java
uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93
with:
distribution: temurin
java-version: 21
- name: Setup gradle
uses: gradle/actions/setup-gradle@9e899d11ad247ec76be7a60bc1cf9d3abbb9e7f1
with:
cache-disabled: true
- name: Assemble release
run:
./gradlew -Prelease.releaseVersion=${{env.RELEASE_VERSION}} -Pversion=${{env.RELEASE_VERSION}} assemble
- name: Hashes
id: hashes
run: |
cd build/distributions
echo "zipSha=$(shasum -a 256 besu*.zip)"
echo "tarSha=$(shasum -a 256 besu*.tar.gz)"
echo "zipSha=$(shasum -a 256 besu*.zip)" >> $GITHUB_OUTPUT
echo "tarSha=$(shasum -a 256 besu*.tar.gz)" >> $GITHUB_OUTPUT
shasum -a 256 besu-${{env.RELEASE_VERSION}}.tar.gz > besu-${{env.RELEASE_VERSION}}.tar.gz.sha256
shasum -a 256 besu-${{env.RELEASE_VERSION}}.zip > besu-${{env.RELEASE_VERSION}}.zip.sha256
- name: Upload tarball
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3
with:
path: 'build/distributions/besu-${{ env.RELEASE_VERSION }}.tar.gz'
name: besu-${{ env.RELEASE_VERSION }}.tar.gz
compression-level: 0
- name: upload zipfile
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3
with:
path: 'build/distributions/besu-${{ env.RELEASE_VERSION }}.zip'
name: besu-${{ env.RELEASE_VERSION }}.zip
compression-level: 0
- name: upload checksum zip
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3
with:
path: 'build/distributions/besu-${{ env.RELEASE_VERSION }}.zip.sha256'
name: besu-${{ env.RELEASE_VERSION }}.zip.sha256
compression-level: 0
- name: upload checksum tar.gz
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3
with:
path: 'build/distributions/besu-${{ env.RELEASE_VERSION }}.tar.gz.sha256'
name: besu-${{ env.RELEASE_VERSION }}.tar.gz.sha256
compression-level: 0
test-windows:
runs-on: windows-2022
needs: ["build"]
timeout-minutes: 5
steps:
- name: Set up Java
uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93
with:
distribution: temurin
java-version: 21
- name: Download zip
uses: actions/download-artifact@eaceaf801fd36c7dee90939fad912460b18a1ffe
with:
pattern: besu-*.zip
merge-multiple: true
- name: Test
run: |
unzip besu-*.zip -d besu-tmp
cd besu-tmp
mv besu-* ../besu
cd ..
besu\bin\besu.bat --help
besu\bin\besu.bat --version
test-linux:
runs-on: ubuntu-22.04
needs: ["build"]
timeout-minutes: 5
steps:
- name: Set up Java
uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93
with:
distribution: temurin
java-version: 21
- name: Download tar.gz
uses: actions/download-artifact@eaceaf801fd36c7dee90939fad912460b18a1ffe
with:
pattern: besu-*.tar.gz
merge-multiple: true
- name: Test
run: |
tar zxvf besu-*.tar.gz
rm -f besu-*.tar.gz
mv besu-* besu-test
besu-test/bin/besu --help
besu-test/bin/besu --version
docker-lint:
runs-on: ubuntu-22.04
needs: [test-linux, test-windows]
env:
RELEASE_VERSION: ${{ needs.validate.outputs.release_version }} # Use the output from the pre_process_release job
steps:
- name: Checkout Repo
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
with:
ref: ${{ env.RELEASE_VERSION }}
- name: hadoLint
run: docker run --rm -i hadolint/hadolint < docker/Dockerfile
docker-publish:
needs: [validate, docker-lint]
env:
RELEASE_VERSION: ${{ needs.validate.outputs.release_version }} # Use the output from the pre_process_release job
strategy:
fail-fast: false
matrix:
platform:
- ubuntu-22.04
- besu-arm64
runs-on: ${{ matrix.platform }}
steps:
- name: Prepare
id: prep
run: |
platform=${{ matrix.platform }}
if [ "$platform" = 'ubuntu-22.04' ]; then
echo "PLATFORM_PAIR=linux-amd64" >> $GITHUB_OUTPUT
echo "ARCH=amd64" >> $GITHUB_OUTPUT
else
echo "PLATFORM_PAIR=linux-arm64" >> $GITHUB_OUTPUT
echo "ARCH=arm64" >> $GITHUB_OUTPUT
fi
- name: Checkout Repo
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
with:
ref: ${{ env.RELEASE_VERSION }}
- name: short sha
id: shortSha
run: echo "sha=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
- name: Set up Java
uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93
with:
distribution: temurin
java-version: 21
- name: setup gradle
uses: gradle/actions/setup-gradle@9e899d11ad247ec76be7a60bc1cf9d3abbb9e7f1
with:
cache-disabled: true
- name: install goss
run: |
mkdir -p docker/reports
curl -L https://github.com/aelsabbahy/goss/releases/download/v0.4.4/goss-${{ steps.prep.outputs.PLATFORM_PAIR }} -o ./docker/tests/goss-${{ steps.prep.outputs.PLATFORM_PAIR }}
- name: login to ${{ env.registry }}
uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d
with:
registry: ${{ env.registry }}
username: ${{ secrets.DOCKER_USER_RW }}
password: ${{ secrets.DOCKER_PASSWORD_RW }}
- name: build and test docker
uses: gradle/actions/setup-gradle@9e899d11ad247ec76be7a60bc1cf9d3abbb9e7f1
env:
architecture: ${{ steps.prep.outputs.ARCH }}
with:
cache-disabled: true
arguments: testDocker -PdockerOrgName=${{ env.registry }}/${{ secrets.DOCKER_ORG }} -Pversion=${{env.RELEASE_VERSION}} -Prelease.releaseVersion=${{ env.RELEASE_VERSION }}
- name: publish
env:
architecture: ${{ steps.prep.outputs.ARCH }}
run: ./gradlew --no-daemon dockerUpload -PdockerOrgName=${{ env.registry }}/${{ secrets.DOCKER_ORG }} -Pversion=${{env.RELEASE_VERSION}} -Prelease.releaseVersion=${{ env.RELEASE_VERSION }}
docker-manifest:
needs: [validate, docker-publish]
runs-on: ubuntu-22.04
env:
RELEASE_VERSION: ${{ needs.validate.outputs.release_version }}
steps:
- name: Checkout Repo
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
with:
ref: ${{ env.RELEASE_VERSION }}
- name: Set up Java
uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93
with:
distribution: temurin
java-version: 21
- name: setup gradle
uses: gradle/actions/setup-gradle@9e899d11ad247ec76be7a60bc1cf9d3abbb9e7f1
with:
cache-disabled: true
- name: login to ${{ env.registry }}
uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d
with:
registry: ${{ env.registry }}
username: ${{ secrets.DOCKER_USER_RW }}
password: ${{ secrets.DOCKER_PASSWORD_RW }}
- name: multi-arch docker
run: ./gradlew manifestDocker -PdockerOrgName=${{ env.registry }}/${{ secrets.DOCKER_ORG }} -Pversion=${{env.RELEASE_VERSION}} -Prelease.releaseVersion=${{ env.RELEASE_VERSION }}
docker-verify:
needs: [validate,docker-manifest]
env:
CONTAINER_NAME: besu-check
RELEASE_VERSION: ${{ needs.validate.outputs.release_version }}
runs-on: ${{ matrix.combination.runner }}
timeout-minutes: 4
strategy:
matrix:
combination:
- tag: ${{ needs.validate.outputs.release_version }}
platform: ''
runner: ubuntu-22.04
- tag: ${{ needs.validate.outputs.release_version }}-amd64
platform: 'linux/amd64'
runner: ubuntu-22.04
- tag: ${{ needs.validate.outputs.release_version }}-arm64
platform: ''
runner: besu-arm64
steps:
- name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
with:
sparse-checkout: '.github/workflows/BesuContainerVerify.sh'
- name: Start container
run: |
PLATFORM_OPT=""
[[ x${{ matrix.combination.platform }} != 'x' ]] && PLATFORM_OPT="--platform ${{ matrix.combination.platform }}"
docker run -d $PLATFORM_OPT --name ${{ env.CONTAINER_NAME }} ${{ secrets.DOCKER_ORG }}/besu:${{ matrix.combination.tag }}
- name: Verify besu container
run: bash .github/workflows/BesuContainerVerify.sh
env:
TAG: ${{ matrix.combination.tag }}
VERSION: ${{ env.RELEASE_VERSION }}
CHECK_LATEST: false
- name: Stop container
run: docker stop ${{ env.CONTAINER_NAME }}
release-draft:
runs-on: ubuntu-22.04
needs: [validate, test-linux, test-windows]
permissions:
contents: write
env:
RELEASE_VERSION: ${{ needs.validate.outputs.release_version }}
steps:
- name: Checkout Repo
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
with:
ref: ${{ env.RELEASE_VERSION }}
- name: Download Besu artifacts
uses: actions/download-artifact@eaceaf801fd36c7dee90939fad912460b18a1ffe
with:
pattern: besu-${{env.RELEASE_VERSION}}*
merge-multiple: true
- name: Draft release notes
run: |
echo "## ${{env.RELEASE_VERSION}}" > draft-release-notes.md
echo "## Upcoming Breaking Changes" >> draft-release-notes.md
echo "## Breaking Changes" >> draft-release-notes.md
echo "## Additions and Improvements" >> draft-release-notes.md
echo "## Bug fixes" >> draft-release-notes.md
echo "`$(cat besu-${{env.RELEASE_VERSION}}.zip.sha256)`" >> draft-release-notes.md
echo "`$(cat besu-${{env.RELEASE_VERSION}}.tar.gz.sha256)`" >> draft-release-notes.md
cat besu-${{env.RELEASE_VERSION}}.zip.sha256 >> draft-release-notes.md
cat besu-${{env.RELEASE_VERSION}}.tar.gz.sha256 >> draft-release-notes.md
- name: Draft release
run: |
gh release create \
--draft \
--title=${{env.RELEASE_VERSION}} \
--notes-file draft-release-notes.md \
--verify-tag ${{env.RELEASE_VERSION}} \
besu-${{env.RELEASE_VERSION}}.tar.gz \
besu-${{env.RELEASE_VERSION}}.zip \
besu-${{env.RELEASE_VERSION}}.zip.sha256 \
besu-${{env.RELEASE_VERSION}}.tar.gz.sha256
env:
GH_TOKEN: ${{ github.token }}
artifactory:
runs-on: ubuntu-22.04
needs: [validate, test-linux, test-windows]
env:
RELEASE_VERSION: ${{ needs.validate.outputs.release_version }}
steps:
- name: checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
with:
ref: ${{ env.RELEASE_VERSION }}
- name: Set up Java
uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93
with:
distribution: temurin
java-version: 21
- name: setup gradle
uses: gradle/actions/setup-gradle@9e899d11ad247ec76be7a60bc1cf9d3abbb9e7f1
with:
cache-disabled: true
- name: Artifactory Publish
env:
ARTIFACTORY_USER: ${{ secrets.BESU_ARTIFACTORY_USER }}
ARTIFACTORY_KEY: ${{ secrets.BESU_ARTIFACTORY_TOKEN }}
run: ./gradlew -Prelease.releaseVersion=${{ env.RELEASE_VERSION }} -Pversion=${{env.RELEASE_VERSION}} artifactoryPublish

@ -1,316 +0,0 @@
name: release
on:
release:
types: [released]
env:
registry: docker.io
GRADLE_OPTS: "-Dorg.gradle.parallel=true -Dorg.gradle.caching=true"
jobs:
preprocess_release:
runs-on: ubuntu-22.04
steps:
- name: Pre-process Release Name
id: pre_process_release_name
env:
RELEASE_NAME: "${{ github.event.release.name }}"
run: |
# strip all whitespace
RELEASE_NAME="${RELEASE_NAME//[[:space:]]/}"
if [[ ! "$RELEASE_NAME" =~ ^[0-9]+\.[0-9]+(\.[0-9]+)?(-.*)?$ ]]; then
echo "Release name does not conform to a valid besu release format YY.M.v[-suffix], e.g. 24.8.0-RC1."
exit 1
fi
echo "release_name=$RELEASE_NAME" >> $GITHUB_OUTPUT # Set as output using the new syntax
outputs:
release_name: ${{ steps.pre_process_release_name.outputs.release_name }}
artifacts:
runs-on: ubuntu-22.04
needs: preprocess_release
env:
RELEASE_NAME: ${{ needs.preprocess_release.outputs.release_name }} # Use the output from the pre_process_release job
permissions:
contents: write
outputs:
tarSha: ${{steps.hashes.outputs.tarSha}}
zipSha: ${{steps.hashes.outputs.zipSha}}
steps:
- name: checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
- name: Set up Java
uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93
with:
distribution: temurin
java-version: 21
- name: setup gradle
uses: gradle/actions/setup-gradle@9e899d11ad247ec76be7a60bc1cf9d3abbb9e7f1
with:
cache-disabled: true
- name: assemble release
run:
./gradlew -Prelease.releaseVersion=${{env.RELEASE_NAME}} -Pversion=${{env.RELEASE_NAME}} assemble
- name: hashes
id: hashes
run: |
cd build/distributions
echo "zipSha=$(shasum -a 256 besu*.zip)"
echo "tarSha=$(shasum -a 256 besu*.tar.gz)"
echo "zipSha=$(shasum -a 256 besu*.zip)" >> $GITHUB_OUTPUT
echo "tarSha=$(shasum -a 256 besu*.tar.gz)" >> $GITHUB_OUTPUT
- name: upload tarball
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3
with:
path: 'build/distributions/besu*.tar.gz'
name: besu-${{ env.RELEASE_NAME }}.tar.gz
compression-level: 0
- name: upload zipfile
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3
with:
path: 'build/distributions/besu*.zip'
name: besu-${{ env.RELEASE_NAME }}.zip
compression-level: 0
testWindows:
runs-on: windows-2022
needs: artifacts
timeout-minutes: 10
steps:
- name: Set up Java
uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93
with:
distribution: temurin
java-version: 21
- name: Download zip
uses: actions/download-artifact@eaceaf801fd36c7dee90939fad912460b18a1ffe
with:
pattern: besu-*.zip
merge-multiple: true
- name: test Besu
run: |
dir
unzip besu-*.zip -d besu-tmp
cd besu-tmp
mv besu-* ../besu
cd ..
besu\bin\besu.bat --help
besu\bin\besu.bat --version
publish:
runs-on: ubuntu-22.04
needs: [preprocess_release, testWindows, artifacts]
env:
RELEASE_NAME: ${{ needs.preprocess_release.outputs.release_name }} # Use the output from the pre_process_release job
permissions:
contents: write
steps:
- name: Download archives
uses: actions/download-artifact@eaceaf801fd36c7dee90939fad912460b18a1ffe
with:
pattern: besu-*
merge-multiple: true
path: 'build/distributions'
- name: Upload Release assets
uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844
with:
append_body: true
files: |
build/distributions/besu*.tar.gz
build/distributions/besu*.zip
body: |
${{needs.artifacts.outputs.tarSha}}
${{needs.artifacts.outputs.zipSha}}
artifactoryPublish:
runs-on: ubuntu-22.04
needs: [preprocess_release, artifacts]
env:
RELEASE_NAME: ${{ needs.preprocess_release.outputs.release_name }} # Use the output from the pre_process_release job
steps:
- name: checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
- name: Set up Java
uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93
with:
distribution: temurin
java-version: 21
- name: setup gradle
uses: gradle/actions/setup-gradle@9e899d11ad247ec76be7a60bc1cf9d3abbb9e7f1
with:
cache-disabled: true
- name: Artifactory Publish
env:
ARTIFACTORY_USER: ${{ secrets.BESU_ARTIFACTORY_USER }}
ARTIFACTORY_KEY: ${{ secrets.BESU_ARTIFACTORY_TOKEN }}
run: ./gradlew -Prelease.releaseVersion=${{ env.RELEASE_NAME }} -Pversion=${{env.RELEASE_NAME}} artifactoryPublish
hadolint:
runs-on: ubuntu-22.04
steps:
- name: Checkout Repo
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
- name: Set up Java
uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93
with:
distribution: temurin
java-version: 21
- name: setup gradle
uses: gradle/actions/setup-gradle@9e899d11ad247ec76be7a60bc1cf9d3abbb9e7f1
with:
cache-disabled: true
- name: hadoLint
run: docker run --rm -i hadolint/hadolint < docker/Dockerfile
buildDocker:
needs: [preprocess_release, hadolint]
env:
RELEASE_NAME: ${{ needs.preprocess_release.outputs.release_name }} # Use the output from the pre_process_release job
permissions:
contents: read
packages: write
strategy:
fail-fast: false
matrix:
platform:
- ubuntu-22.04
- besu-arm64
runs-on: ${{ matrix.platform }}
steps:
- name: Prepare
id: prep
run: |
platform=${{ matrix.platform }}
if [ "$platform" = 'ubuntu-22.04' ]; then
echo "PLATFORM_PAIR=linux-amd64" >> $GITHUB_OUTPUT
echo "ARCH=amd64" >> $GITHUB_OUTPUT
else
echo "PLATFORM_PAIR=linux-arm64" >> $GITHUB_OUTPUT
echo "ARCH=arm64" >> $GITHUB_OUTPUT
fi
- name: Checkout Repo
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
- name: short sha
id: shortSha
run: echo "sha=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
- name: Set up Java
uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93
with:
distribution: temurin
java-version: 21
- name: setup gradle
uses: gradle/actions/setup-gradle@9e899d11ad247ec76be7a60bc1cf9d3abbb9e7f1
with:
cache-disabled: true
- name: install goss
run: |
mkdir -p docker/reports
curl -L https://github.com/aelsabbahy/goss/releases/download/v0.4.4/goss-${{ steps.prep.outputs.PLATFORM_PAIR }} -o ./docker/tests/goss-${{ steps.prep.outputs.PLATFORM_PAIR }}
- name: login to ${{ env.registry }}
uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d
with:
registry: ${{ env.registry }}
username: ${{ secrets.DOCKER_USER_RW }}
password: ${{ secrets.DOCKER_PASSWORD_RW }}
- name: build and test docker
uses: gradle/actions/setup-gradle@9e899d11ad247ec76be7a60bc1cf9d3abbb9e7f1
env:
architecture: ${{ steps.prep.outputs.ARCH }}
with:
cache-disabled: true
arguments: testDocker -PdockerOrgName=${{ env.registry }}/${{ secrets.DOCKER_ORG }} -Pversion=${{env.RELEASE_NAME}} -Prelease.releaseVersion=${{ env.RELEASE_NAME }}
- name: publish
env:
architecture: ${{ steps.prep.outputs.ARCH }}
run: ./gradlew --no-daemon dockerUpload -PdockerOrgName=${{ env.registry }}/${{ secrets.DOCKER_ORG }} -Pversion=${{env.RELEASE_NAME}} -Prelease.releaseVersion=${{ env.RELEASE_NAME }}
multiArch:
needs: [preprocess_release, buildDocker]
env:
RELEASE_NAME: ${{ needs.preprocess_release.outputs.release_name }} # Use the output from the pre_process_release job
runs-on: ubuntu-22.04
permissions:
contents: read
packages: write
steps:
- name: Checkout Repo
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
- name: Set up Java
uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93
with:
distribution: temurin
java-version: 21
- name: setup gradle
uses: gradle/actions/setup-gradle@9e899d11ad247ec76be7a60bc1cf9d3abbb9e7f1
with:
cache-disabled: true
- name: login to ${{ env.registry }}
uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d
with:
registry: ${{ env.registry }}
username: ${{ secrets.DOCKER_USER_RW }}
password: ${{ secrets.DOCKER_PASSWORD_RW }}
- name: multi-arch docker
run: ./gradlew manifestDocker -PdockerOrgName=${{ env.registry }}/${{ secrets.DOCKER_ORG }} -Pversion=${{env.RELEASE_NAME}} -Prelease.releaseVersion=${{ env.RELEASE_NAME }}
amendNotes:
needs: [preprocess_release, multiArch]
env:
RELEASE_NAME: ${{ needs.preprocess_release.outputs.release_name }} # Use the output from the pre_process_release job
runs-on: ubuntu-22.04
permissions:
contents: write
steps:
- name: add pull command to release notes
uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844
with:
append_body: true
body: |
`docker pull ${{env.registry}}/${{secrets.DOCKER_ORG}}/besu:${{env.RELEASE_NAME}}`
dockerPromoteX64:
needs: [preprocess_release, multiArch]
env:
RELEASE_NAME: ${{ needs.preprocess_release.outputs.release_name }} # Use the output from the pre_process_release job
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
- uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93
with:
distribution: temurin
java-version: 21
cache: gradle
- name: login to ${{ env.registry }}
uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d
with:
registry: ${{ env.registry }}
username: ${{ secrets.DOCKER_USER_RW }}
password: ${{ secrets.DOCKER_PASSWORD_RW }}
- name: Setup Gradle
uses: gradle/actions/setup-gradle@9e899d11ad247ec76be7a60bc1cf9d3abbb9e7f1
with:
cache-disabled: true
- name: Docker upload
run: ./gradlew "-Prelease.releaseVersion=${{ env.RELEASE_NAME }}" "-PdockerOrgName=${{ env.registry }}/${{ secrets.DOCKER_ORG }}" dockerUploadRelease
- name: Docker manifest
run: ./gradlew "-Prelease.releaseVersion=${{ env.RELEASE_NAME }}" "-PdockerOrgName=${{ env.registry }}/${{ secrets.DOCKER_ORG }}" manifestDockerRelease
verifyContainer:
needs: [preprocess_release, dockerPromoteX64]
env:
RELEASE_NAME: ${{ needs.preprocess_release.outputs.release_name }} # Use the output from the pre_process_release job
runs-on: ubuntu-22.04
permissions:
contents: read
actions: write
steps:
- name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
- name: Trigger container verify
run: echo '{"version":"${{ env.RELEASE_NAME }}","verify-latest-version":"true"}' | gh workflow run container-verify.yml --json
env:
GH_TOKEN: ${{ github.token }}

@ -15,6 +15,8 @@
- LUKSO Cancun Hardfork [#7686](https://github.com/hyperledger/besu/pull/7686) - LUKSO Cancun Hardfork [#7686](https://github.com/hyperledger/besu/pull/7686)
- Add configuration of Consolidation Request Contract Address via genesis configuration [#7647](https://github.com/hyperledger/besu/pull/7647) - Add configuration of Consolidation Request Contract Address via genesis configuration [#7647](https://github.com/hyperledger/besu/pull/7647)
- Interrupt pending transaction processing on block creation timeout [#7673](https://github.com/hyperledger/besu/pull/7673) - Interrupt pending transaction processing on block creation timeout [#7673](https://github.com/hyperledger/besu/pull/7673)
- Align gas cap calculation for transaction simulation to Geth approach [#7703](https://github.com/hyperledger/besu/pull/7703)
- Expose chainId in the `BlockchainService` [7702](https://github.com/hyperledger/besu/pull/7702)
### Bug fixes ### Bug fixes
- Fix mounted data path directory permissions for besu user [#7575](https://github.com/hyperledger/besu/pull/7575) - Fix mounted data path directory permissions for besu user [#7575](https://github.com/hyperledger/besu/pull/7575)

@ -9,19 +9,16 @@
| Name | Github | LFID | | Name | Github | LFID |
| ---------------- | ---------------- | ---------------- | | ---------------- | ---------------- | ---------------- |
| Ameziane Hamlat | ahamlat | ahamlat | | Ameziane Hamlat | ahamlat | ahamlat |
| Adrian Sutton | ajsutton | ajsutton |
| Antony Denyer | antonydenyer | antonydenyer |
| Antoine Toulme | atoulme | atoulme |
| Daniel Lehrner | daniellehrner | daniellehrner | | Daniel Lehrner | daniellehrner | daniellehrner |
| Diego López León | diega | diega | | Diego López León | diega | diega |
| Fabio Di Fabio | fab-10 | fab-10 | | Fabio Di Fabio | fab-10 | fab-10 |
| Gabriel Trintinalia | gabriel-trintinalia | gabrieltrintinalia | | Gabriel Trintinalia | gabriel-trintinalia | gabrieltrintinalia |
| Gary Schulte | garyschulte | GarySchulte | | Gary Schulte | garyschulte | GarySchulte |
| Jiri Peinlich | gezero | JiriPeinlich |
| Gabriel Fukushima| gfukushima | gfukushima | | Gabriel Fukushima| gfukushima | gfukushima |
| Justin Florentine| jflo | RoboCopsGoneMad | | Justin Florentine| jflo | RoboCopsGoneMad |
| Jason Frame | jframe | jframe | | Jason Frame | jframe | jframe |
| Joshua Fernandes | joshuafernandes | joshuafernandes | | Joshua Fernandes | joshuafernandes | joshuafernandes |
| Luis Pinto | lu-pinto | lu-pinto
| Lucas Saldanha | lucassaldanha | lucassaldanha | | Lucas Saldanha | lucassaldanha | lucassaldanha |
| Sally MacFarlane | macfarla | macfarla | | Sally MacFarlane | macfarla | macfarla |
| Karim Taam | matkt | matkt | | Karim Taam | matkt | matkt |
@ -37,11 +34,15 @@
| Name | Github | LFID | | Name | Github | LFID |
|------------------|------------------|------------------| |------------------|------------------|------------------|
| Abdel Bakhta | abdelhamidbakhta | abdelhamidbakhta | | Abdel Bakhta | abdelhamidbakhta | abdelhamidbakhta |
| Adrian Sutton | ajsutton | ajsutton |
| Antony Denyer | antonydenyer | antonydenyer |
| Antoine Toulme | atoulme | atoulme |
| Byron Gravenorst | bgravenorst | bgravenorst | | Byron Gravenorst | bgravenorst | bgravenorst |
| Chris Hare | CjHare | cjhare | | Chris Hare | CjHare | cjhare |
| David Mechler | davemec | davemec | | David Mechler | davemec | davemec |
| Edward Evans | EdJoJob | EdJoJob | | Edward Evans | EdJoJob | EdJoJob |
| Edward Mack | edwardmack | mackcom | | Edward Mack | edwardmack | mackcom |
| Jiri Peinlich | gezero | JiriPeinlich |
| Frank Li | frankisawesome | frankliawesome | | Frank Li | frankisawesome | frankliawesome |
| Ivaylo Kirilov | iikirilov | iikirilov | | Ivaylo Kirilov | iikirilov | iikirilov |
| Madeline Murray | MadelineMurray | madelinemurray | | Madeline Murray | MadelineMurray | madelinemurray |

@ -128,6 +128,7 @@ public class BesuNode implements NodeConfiguration, RunnableNode, AutoCloseable
private boolean useWsForJsonRpc = false; private boolean useWsForJsonRpc = false;
private String token = null; private String token = null;
private final List<String> plugins = new ArrayList<>(); private final List<String> plugins = new ArrayList<>();
private final List<String> requestedPlugins;
private final List<String> extraCLIOptions; private final List<String> extraCLIOptions;
private final List<String> staticNodes; private final List<String> staticNodes;
private boolean isDnsEnabled = false; private boolean isDnsEnabled = false;
@ -163,6 +164,7 @@ public class BesuNode implements NodeConfiguration, RunnableNode, AutoCloseable
final boolean secp256k1Native, final boolean secp256k1Native,
final boolean altbn128Native, final boolean altbn128Native,
final List<String> plugins, final List<String> plugins,
final List<String> requestedPlugins,
final List<String> extraCLIOptions, final List<String> extraCLIOptions,
final List<String> staticNodes, final List<String> staticNodes,
final boolean isDnsEnabled, final boolean isDnsEnabled,
@ -224,6 +226,7 @@ public class BesuNode implements NodeConfiguration, RunnableNode, AutoCloseable
LOG.error("Could not find plugin \"{}\" in resources", pluginName); LOG.error("Could not find plugin \"{}\" in resources", pluginName);
} }
}); });
this.requestedPlugins = requestedPlugins;
engineRpcConfiguration.ifPresent( engineRpcConfiguration.ifPresent(
config -> MergeConfigOptions.setMergeEnabled(config.isEnabled())); config -> MergeConfigOptions.setMergeEnabled(config.isEnabled()));
this.extraCLIOptions = extraCLIOptions; this.extraCLIOptions = extraCLIOptions;
@ -738,6 +741,10 @@ public class BesuNode implements NodeConfiguration, RunnableNode, AutoCloseable
return plugins; return plugins;
} }
public List<String> getRequestedPlugins() {
return requestedPlugins;
}
@Override @Override
public List<String> getExtraCLIOptions() { public List<String> getExtraCLIOptions() {
return extraCLIOptions; return extraCLIOptions;

@ -17,8 +17,8 @@ package org.hyperledger.besu.tests.acceptance.dsl.node;
import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Preconditions.checkState;
import static java.nio.charset.StandardCharsets.UTF_8; import static java.nio.charset.StandardCharsets.UTF_8;
import org.hyperledger.besu.cli.options.DataStorageOptions;
import org.hyperledger.besu.cli.options.TransactionPoolOptions; import org.hyperledger.besu.cli.options.TransactionPoolOptions;
import org.hyperledger.besu.cli.options.stable.DataStorageOptions;
import org.hyperledger.besu.cli.options.unstable.NetworkingOptions; import org.hyperledger.besu.cli.options.unstable.NetworkingOptions;
import org.hyperledger.besu.ethereum.api.jsonrpc.ipc.JsonRpcIpcConfiguration; import org.hyperledger.besu.ethereum.api.jsonrpc.ipc.JsonRpcIpcConfiguration;
import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration; import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration;
@ -451,6 +451,11 @@ public class ProcessBesuNodeRunner implements BesuNodeRunner {
params.add("--logging=" + level); params.add("--logging=" + level);
} }
if (!node.getRequestedPlugins().isEmpty()) {
params.add(
"--plugins=" + node.getRequestedPlugins().stream().collect(Collectors.joining(",")));
}
params.addAll(node.getRunCommand()); params.addAll(node.getRunCommand());
return params; return params;
} }

@ -39,6 +39,7 @@ import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.core.ImmutableMiningParameters; import org.hyperledger.besu.ethereum.core.ImmutableMiningParameters;
import org.hyperledger.besu.ethereum.core.MiningParameters; import org.hyperledger.besu.ethereum.core.MiningParameters;
import org.hyperledger.besu.ethereum.core.plugins.PluginConfiguration; import org.hyperledger.besu.ethereum.core.plugins.PluginConfiguration;
import org.hyperledger.besu.ethereum.core.plugins.PluginInfo;
import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration; import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration;
import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration; import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration;
import org.hyperledger.besu.ethereum.eth.transactions.BlobCacheModule; import org.hyperledger.besu.ethereum.eth.transactions.BlobCacheModule;
@ -302,6 +303,12 @@ public class ThreadBesuNodeRunner implements BesuNodeRunner {
return toProvide.getExtraCLIOptions(); return toProvide.getExtraCLIOptions();
} }
@Provides
@Named("RequestedPlugins")
public List<String> provideRequestedPlugins() {
return toProvide.getRequestedPlugins();
}
@Provides @Provides
Path provideDataDir() { Path provideDataDir() {
return toProvide.homeDirectory(); return toProvide.homeDirectory();
@ -469,7 +476,8 @@ public class ThreadBesuNodeRunner implements BesuNodeRunner {
final RpcEndpointServiceImpl rpcEndpointServiceImpl, final RpcEndpointServiceImpl rpcEndpointServiceImpl,
final BesuConfiguration commonPluginConfiguration, final BesuConfiguration commonPluginConfiguration,
final PermissioningServiceImpl permissioningService, final PermissioningServiceImpl permissioningService,
final @Named("ExtraCLIOptions") List<String> extraCLIOptions) { final @Named("ExtraCLIOptions") List<String> extraCLIOptions,
final @Named("RequestedPlugins") List<String> requestedPlugins) {
final CommandLine commandLine = new CommandLine(CommandSpec.create()); final CommandLine commandLine = new CommandLine(CommandSpec.create());
final BesuPluginContextImpl besuPluginContext = new BesuPluginContextImpl(); final BesuPluginContextImpl besuPluginContext = new BesuPluginContextImpl();
besuPluginContext.addService(StorageService.class, storageService); besuPluginContext.addService(StorageService.class, storageService);
@ -504,7 +512,10 @@ public class ThreadBesuNodeRunner implements BesuNodeRunner {
besuPluginContext.addService(PrivacyPluginService.class, new PrivacyPluginServiceImpl()); besuPluginContext.addService(PrivacyPluginService.class, new PrivacyPluginServiceImpl());
besuPluginContext.initialize( besuPluginContext.initialize(
new PluginConfiguration.Builder().pluginsDir(pluginsPath).build()); new PluginConfiguration.Builder()
.pluginsDir(pluginsPath)
.requestedPlugins(requestedPlugins.stream().map(PluginInfo::new).toList())
.build());
besuPluginContext.registerPlugins(); besuPluginContext.registerPlugins();
commandLine.parseArgs(extraCLIOptions.toArray(new String[0])); commandLine.parseArgs(extraCLIOptions.toArray(new String[0]));

@ -64,6 +64,7 @@ public class BesuNodeConfiguration {
private final boolean secp256k1Native; private final boolean secp256k1Native;
private final boolean altbn128Native; private final boolean altbn128Native;
private final List<String> plugins; private final List<String> plugins;
private final List<String> requestedPlugins;
private final List<String> extraCLIOptions; private final List<String> extraCLIOptions;
private final List<String> staticNodes; private final List<String> staticNodes;
private final boolean isDnsEnabled; private final boolean isDnsEnabled;
@ -102,6 +103,7 @@ public class BesuNodeConfiguration {
final boolean secp256k1Native, final boolean secp256k1Native,
final boolean altbn128Native, final boolean altbn128Native,
final List<String> plugins, final List<String> plugins,
final List<String> requestedPlugins,
final List<String> extraCLIOptions, final List<String> extraCLIOptions,
final List<String> staticNodes, final List<String> staticNodes,
final boolean isDnsEnabled, final boolean isDnsEnabled,
@ -137,6 +139,7 @@ public class BesuNodeConfiguration {
this.secp256k1Native = secp256k1Native; this.secp256k1Native = secp256k1Native;
this.altbn128Native = altbn128Native; this.altbn128Native = altbn128Native;
this.plugins = plugins; this.plugins = plugins;
this.requestedPlugins = requestedPlugins;
this.extraCLIOptions = extraCLIOptions; this.extraCLIOptions = extraCLIOptions;
this.staticNodes = staticNodes; this.staticNodes = staticNodes;
this.isDnsEnabled = isDnsEnabled; this.isDnsEnabled = isDnsEnabled;
@ -239,6 +242,10 @@ public class BesuNodeConfiguration {
return plugins; return plugins;
} }
public List<String> getRequestedPlugins() {
return requestedPlugins;
}
public List<String> getExtraCLIOptions() { public List<String> getExtraCLIOptions() {
return extraCLIOptions; return extraCLIOptions;
} }

@ -93,6 +93,7 @@ public class BesuNodeConfigurationBuilder {
private boolean secp256K1Native = true; private boolean secp256K1Native = true;
private boolean altbn128Native = true; private boolean altbn128Native = true;
private final List<String> plugins = new ArrayList<>(); private final List<String> plugins = new ArrayList<>();
private final List<String> requestedPlugins = new ArrayList<>();
private final List<String> extraCLIOptions = new ArrayList<>(); private final List<String> extraCLIOptions = new ArrayList<>();
private List<String> staticNodes = new ArrayList<>(); private List<String> staticNodes = new ArrayList<>();
private boolean isDnsEnabled = false; private boolean isDnsEnabled = false;
@ -448,6 +449,12 @@ public class BesuNodeConfigurationBuilder {
return this; return this;
} }
public BesuNodeConfigurationBuilder requestedPlugins(final List<String> requestedPlugins) {
this.requestedPlugins.clear();
this.requestedPlugins.addAll(requestedPlugins);
return this;
}
public BesuNodeConfigurationBuilder extraCLIOptions(final List<String> extraCLIOptions) { public BesuNodeConfigurationBuilder extraCLIOptions(final List<String> extraCLIOptions) {
this.extraCLIOptions.clear(); this.extraCLIOptions.clear();
this.extraCLIOptions.addAll(extraCLIOptions); this.extraCLIOptions.addAll(extraCLIOptions);
@ -545,6 +552,7 @@ public class BesuNodeConfigurationBuilder {
secp256K1Native, secp256K1Native,
altbn128Native, altbn128Native,
plugins, plugins,
requestedPlugins,
extraCLIOptions, extraCLIOptions,
staticNodes, staticNodes,
isDnsEnabled, isDnsEnabled,

@ -77,6 +77,7 @@ public class BesuNodeFactory {
config.isSecp256k1Native(), config.isSecp256k1Native(),
config.isAltbn128Native(), config.isAltbn128Native(),
config.getPlugins(), config.getPlugins(),
config.getRequestedPlugins(),
config.getExtraCLIOptions(), config.getExtraCLIOptions(),
config.getStaticNodes(), config.getStaticNodes(),
config.isDnsEnabled(), config.isDnsEnabled(),

@ -24,7 +24,6 @@ import static org.hyperledger.besu.cli.config.NetworkName.MAINNET;
import static org.hyperledger.besu.cli.util.CommandLineUtils.DEPENDENCY_WARNING_MSG; import static org.hyperledger.besu.cli.util.CommandLineUtils.DEPENDENCY_WARNING_MSG;
import static org.hyperledger.besu.cli.util.CommandLineUtils.isOptionSet; import static org.hyperledger.besu.cli.util.CommandLineUtils.isOptionSet;
import static org.hyperledger.besu.controller.BesuController.DATABASE_PATH; import static org.hyperledger.besu.controller.BesuController.DATABASE_PATH;
import static org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcConfiguration.DEFAULT_ENGINE_JSON_RPC_PORT;
import static org.hyperledger.besu.ethereum.api.jsonrpc.authentication.EngineAuthService.EPHEMERAL_JWT_FILE; import static org.hyperledger.besu.ethereum.api.jsonrpc.authentication.EngineAuthService.EPHEMERAL_JWT_FILE;
import static org.hyperledger.besu.nat.kubernetes.KubernetesNatManager.DEFAULT_BESU_SERVICE_NAME_FILTER; import static org.hyperledger.besu.nat.kubernetes.KubernetesNatManager.DEFAULT_BESU_SERVICE_NAME_FILTER;
@ -38,22 +37,22 @@ import org.hyperledger.besu.cli.config.EthNetworkConfig;
import org.hyperledger.besu.cli.config.NetworkName; import org.hyperledger.besu.cli.config.NetworkName;
import org.hyperledger.besu.cli.config.ProfilesCompletionCandidates; import org.hyperledger.besu.cli.config.ProfilesCompletionCandidates;
import org.hyperledger.besu.cli.converter.MetricCategoryConverter; import org.hyperledger.besu.cli.converter.MetricCategoryConverter;
import org.hyperledger.besu.cli.converter.PercentageConverter;
import org.hyperledger.besu.cli.converter.SubnetInfoConverter;
import org.hyperledger.besu.cli.custom.JsonRPCAllowlistHostsProperty; import org.hyperledger.besu.cli.custom.JsonRPCAllowlistHostsProperty;
import org.hyperledger.besu.cli.error.BesuExecutionExceptionHandler; import org.hyperledger.besu.cli.error.BesuExecutionExceptionHandler;
import org.hyperledger.besu.cli.error.BesuParameterExceptionHandler; import org.hyperledger.besu.cli.error.BesuParameterExceptionHandler;
import org.hyperledger.besu.cli.options.DataStorageOptions;
import org.hyperledger.besu.cli.options.MiningOptions; import org.hyperledger.besu.cli.options.MiningOptions;
import org.hyperledger.besu.cli.options.TransactionPoolOptions; import org.hyperledger.besu.cli.options.TransactionPoolOptions;
import org.hyperledger.besu.cli.options.stable.ApiConfigurationOptions; import org.hyperledger.besu.cli.options.stable.ApiConfigurationOptions;
import org.hyperledger.besu.cli.options.stable.DataStorageOptions; import org.hyperledger.besu.cli.options.stable.EngineRPCConfiguration;
import org.hyperledger.besu.cli.options.stable.EngineRPCOptions;
import org.hyperledger.besu.cli.options.stable.EthstatsOptions; import org.hyperledger.besu.cli.options.stable.EthstatsOptions;
import org.hyperledger.besu.cli.options.stable.GraphQlOptions; import org.hyperledger.besu.cli.options.stable.GraphQlOptions;
import org.hyperledger.besu.cli.options.stable.JsonRpcHttpOptions; import org.hyperledger.besu.cli.options.stable.JsonRpcHttpOptions;
import org.hyperledger.besu.cli.options.stable.LoggingLevelOption; import org.hyperledger.besu.cli.options.stable.LoggingLevelOption;
import org.hyperledger.besu.cli.options.stable.MetricsOptionGroup; import org.hyperledger.besu.cli.options.stable.MetricsOptionGroup;
import org.hyperledger.besu.cli.options.stable.NodePrivateKeyFileOption; import org.hyperledger.besu.cli.options.stable.NodePrivateKeyFileOption;
import org.hyperledger.besu.cli.options.stable.P2PTLSConfigOptions; import org.hyperledger.besu.cli.options.stable.P2PDiscoveryOptions;
import org.hyperledger.besu.cli.options.stable.PermissionsOptions; import org.hyperledger.besu.cli.options.stable.PermissionsOptions;
import org.hyperledger.besu.cli.options.stable.PluginsConfigurationOptions; import org.hyperledger.besu.cli.options.stable.PluginsConfigurationOptions;
import org.hyperledger.besu.cli.options.stable.RpcWebsocketOptions; import org.hyperledger.besu.cli.options.stable.RpcWebsocketOptions;
@ -67,6 +66,7 @@ import org.hyperledger.besu.cli.options.unstable.MetricsCLIOptions;
import org.hyperledger.besu.cli.options.unstable.NatOptions; import org.hyperledger.besu.cli.options.unstable.NatOptions;
import org.hyperledger.besu.cli.options.unstable.NativeLibraryOptions; import org.hyperledger.besu.cli.options.unstable.NativeLibraryOptions;
import org.hyperledger.besu.cli.options.unstable.NetworkingOptions; import org.hyperledger.besu.cli.options.unstable.NetworkingOptions;
import org.hyperledger.besu.cli.options.unstable.P2PTLSConfigOptions;
import org.hyperledger.besu.cli.options.unstable.PrivacyPluginOptions; import org.hyperledger.besu.cli.options.unstable.PrivacyPluginOptions;
import org.hyperledger.besu.cli.options.unstable.RPCOptions; import org.hyperledger.besu.cli.options.unstable.RPCOptions;
import org.hyperledger.besu.cli.options.unstable.SynchronizerOptions; import org.hyperledger.besu.cli.options.unstable.SynchronizerOptions;
@ -124,6 +124,7 @@ import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolCo
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration;
import org.hyperledger.besu.ethereum.mainnet.FrontierTargetingGasLimitCalculator; import org.hyperledger.besu.ethereum.mainnet.FrontierTargetingGasLimitCalculator;
import org.hyperledger.besu.ethereum.p2p.config.DiscoveryConfiguration; import org.hyperledger.besu.ethereum.p2p.config.DiscoveryConfiguration;
import org.hyperledger.besu.ethereum.p2p.discovery.P2PDiscoveryConfiguration;
import org.hyperledger.besu.ethereum.p2p.peers.EnodeDnsConfiguration; import org.hyperledger.besu.ethereum.p2p.peers.EnodeDnsConfiguration;
import org.hyperledger.besu.ethereum.p2p.peers.EnodeURLImpl; import org.hyperledger.besu.ethereum.p2p.peers.EnodeURLImpl;
import org.hyperledger.besu.ethereum.p2p.peers.StaticNodesParser; import org.hyperledger.besu.ethereum.p2p.peers.StaticNodesParser;
@ -209,7 +210,6 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.math.BigInteger; import java.math.BigInteger;
import java.net.InetAddress;
import java.net.SocketException; import java.net.SocketException;
import java.net.URI; import java.net.URI;
import java.net.URL; import java.net.URL;
@ -247,7 +247,6 @@ import io.vertx.core.Vertx;
import io.vertx.core.VertxOptions; import io.vertx.core.VertxOptions;
import io.vertx.core.json.DecodeException; import io.vertx.core.json.DecodeException;
import io.vertx.core.metrics.MetricsOptions; import io.vertx.core.metrics.MetricsOptions;
import org.apache.commons.net.util.SubnetUtils.SubnetInfo;
import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.units.bigints.UInt256; import org.apache.tuweni.units.bigints.UInt256;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -416,7 +415,9 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
// P2P Discovery Option Group // P2P Discovery Option Group
@CommandLine.ArgGroup(validate = false, heading = "@|bold P2P Discovery Options|@%n") @CommandLine.ArgGroup(validate = false, heading = "@|bold P2P Discovery Options|@%n")
P2PDiscoveryOptionGroup p2PDiscoveryOptionGroup = new P2PDiscoveryOptionGroup(); P2PDiscoveryOptions p2PDiscoveryOptions = new P2PDiscoveryOptions();
P2PDiscoveryConfiguration p2PDiscoveryConfig;
private final TransactionSelectionServiceImpl transactionSelectionServiceImpl; private final TransactionSelectionServiceImpl transactionSelectionServiceImpl;
private final TransactionPoolValidatorServiceImpl transactionValidatorServiceImpl; private final TransactionPoolValidatorServiceImpl transactionValidatorServiceImpl;
@ -424,163 +425,6 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
private final BlockchainServiceImpl blockchainServiceImpl; private final BlockchainServiceImpl blockchainServiceImpl;
private BesuComponent besuComponent; private BesuComponent besuComponent;
static class P2PDiscoveryOptionGroup {
// Public IP stored to prevent having to research it each time we need it.
private InetAddress autoDiscoveredDefaultIP = null;
// Completely disables P2P within Besu.
@Option(
names = {"--p2p-enabled"},
description = "Enable P2P functionality (default: ${DEFAULT-VALUE})",
arity = "1")
private final Boolean p2pEnabled = true;
// Boolean option to indicate if peers should NOT be discovered, default to
// false indicates that
// the peers should be discovered by default.
//
// This negative option is required because of the nature of the option that is
// true when
// added on the command line. You can't do --option=false, so false is set as
// default
// and you have not to set the option at all if you want it false.
// This seems to be the only way it works with Picocli.
// Also many other software use the same negative option scheme for false
// defaults
// meaning that it's probably the right way to handle disabling options.
@Option(
names = {"--discovery-enabled"},
description = "Enable P2P discovery (default: ${DEFAULT-VALUE})",
arity = "1")
private final Boolean peerDiscoveryEnabled = true;
// A list of bootstrap nodes can be passed
// and a hardcoded list will be used otherwise by the Runner.
// NOTE: we have no control over default value here.
@Option(
names = {"--bootnodes"},
paramLabel = "<enode://id@host:port>",
description =
"Comma separated enode URLs for P2P discovery bootstrap. "
+ "Default is a predefined list.",
split = ",",
arity = "0..*")
private final List<String> bootNodes = null;
@SuppressWarnings({"FieldCanBeFinal", "FieldMayBeFinal"}) // PicoCLI requires non-final Strings.
@Option(
names = {"--p2p-host"},
paramLabel = MANDATORY_HOST_FORMAT_HELP,
description = "IP address this node advertises to its peers (default: ${DEFAULT-VALUE})",
arity = "1")
private String p2pHost = autoDiscoverDefaultIP().getHostAddress();
@SuppressWarnings({"FieldCanBeFinal", "FieldMayBeFinal"}) // PicoCLI requires non-final Strings.
@Option(
names = {"--p2p-interface"},
paramLabel = MANDATORY_HOST_FORMAT_HELP,
description =
"The network interface address on which this node listens for P2P communication (default: ${DEFAULT-VALUE})",
arity = "1")
private String p2pInterface = NetworkUtility.INADDR_ANY;
@Option(
names = {"--p2p-port"},
paramLabel = MANDATORY_PORT_FORMAT_HELP,
description = "Port on which to listen for P2P communication (default: ${DEFAULT-VALUE})",
arity = "1")
private final Integer p2pPort = EnodeURLImpl.DEFAULT_LISTENING_PORT;
@Option(
names = {"--max-peers", "--p2p-peer-upper-bound"},
paramLabel = MANDATORY_INTEGER_FORMAT_HELP,
description = "Maximum P2P connections that can be established (default: ${DEFAULT-VALUE})")
private final Integer maxPeers = DEFAULT_MAX_PEERS;
@Option(
names = {"--remote-connections-limit-enabled"},
description =
"Whether to limit the number of P2P connections initiated remotely. (default: ${DEFAULT-VALUE})")
private final Boolean isLimitRemoteWireConnectionsEnabled = true;
@Option(
names = {"--remote-connections-max-percentage"},
paramLabel = MANDATORY_DOUBLE_FORMAT_HELP,
description =
"The maximum percentage of P2P connections that can be initiated remotely. Must be between 0 and 100 inclusive. (default: ${DEFAULT-VALUE})",
arity = "1",
converter = PercentageConverter.class)
private final Percentage maxRemoteConnectionsPercentage =
Fraction.fromFloat(DEFAULT_FRACTION_REMOTE_WIRE_CONNECTIONS_ALLOWED).toPercentage();
@SuppressWarnings({"FieldCanBeFinal", "FieldMayBeFinal"}) // PicoCLI requires non-final Strings.
@CommandLine.Option(
names = {"--discovery-dns-url"},
description = "Specifies the URL to use for DNS discovery")
private String discoveryDnsUrl = null;
@Option(
names = {"--random-peer-priority-enabled"},
description =
"Allow for incoming connections to be prioritized randomly. This will prevent (typically small, stable) networks from forming impenetrable peer cliques. (default: ${DEFAULT-VALUE})")
private final Boolean randomPeerPriority = Boolean.FALSE;
@Option(
names = {"--banned-node-ids", "--banned-node-id"},
paramLabel = MANDATORY_NODE_ID_FORMAT_HELP,
description = "A list of node IDs to ban from the P2P network.",
split = ",",
arity = "1..*")
void setBannedNodeIds(final List<String> values) {
try {
bannedNodeIds =
values.stream()
.filter(value -> !value.isEmpty())
.map(EnodeURLImpl::parseNodeId)
.collect(Collectors.toList());
} catch (final IllegalArgumentException e) {
throw new ParameterException(
new CommandLine(this),
"Invalid ids supplied to '--banned-node-ids'. " + e.getMessage());
}
}
// Boolean option to set that in a PoA network the bootnodes should always be queried during
// peer table refresh. If this flag is disabled bootnodes are only sent FINDN requests on first
// startup, meaning that an offline bootnode or network outage at the client can prevent it
// discovering any peers without a restart.
@Option(
names = {"--poa-discovery-retry-bootnodes"},
description =
"Always use of bootnodes for discovery in PoA networks. Disabling this reverts "
+ " to the same behaviour as non-PoA networks, where neighbours are only discovered from bootnodes on first startup."
+ "(default: ${DEFAULT-VALUE})",
arity = "1")
private final Boolean poaDiscoveryRetryBootnodes = true;
private Collection<Bytes> bannedNodeIds = new ArrayList<>();
// Used to discover the default IP of the client.
// Loopback IP is used by default as this is how smokeTests require it to be
// and it's probably a good security behaviour to default only on the localhost.
private InetAddress autoDiscoverDefaultIP() {
autoDiscoveredDefaultIP =
Optional.ofNullable(autoDiscoveredDefaultIP).orElseGet(InetAddress::getLoopbackAddress);
return autoDiscoveredDefaultIP;
}
@Option(
names = {"--net-restrict"},
arity = "1..*",
split = ",",
converter = SubnetInfoConverter.class,
description =
"Comma-separated list of allowed IP subnets (e.g., '192.168.1.0/24,10.0.0.0/8').")
private List<SubnetInfo> allowedSubnets;
}
@Option( @Option(
names = {"--sync-mode"}, names = {"--sync-mode"},
paramLabel = MANDATORY_MODE_FORMAT_HELP, paramLabel = MANDATORY_MODE_FORMAT_HELP,
@ -647,42 +491,9 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
// Engine JSON-PRC Options // Engine JSON-PRC Options
@CommandLine.ArgGroup(validate = false, heading = "@|bold Engine JSON-RPC Options|@%n") @CommandLine.ArgGroup(validate = false, heading = "@|bold Engine JSON-RPC Options|@%n")
EngineRPCOptionGroup engineRPCOptionGroup = new EngineRPCOptionGroup(); EngineRPCOptions engineRPCOptions = new EngineRPCOptions();
static class EngineRPCOptionGroup {
@Option(
names = {"--engine-rpc-enabled"},
description =
"enable the engine api, even in the absence of merge-specific configurations.")
private final Boolean overrideEngineRpcEnabled = false;
@Option(
names = {"--engine-rpc-port", "--engine-rpc-http-port"},
paramLabel = MANDATORY_PORT_FORMAT_HELP,
description = "Port to provide consensus client APIS on (default: ${DEFAULT-VALUE})",
arity = "1")
private final Integer engineRpcPort = DEFAULT_ENGINE_JSON_RPC_PORT;
@Option( EngineRPCConfiguration engineRPCConfig = engineRPCOptions.toDomainObject();
names = {"--engine-jwt-secret"},
paramLabel = MANDATORY_FILE_FORMAT_HELP,
description = "Path to file containing shared secret key for JWT signature verification")
private final Path engineJwtKeyFile = null;
@Option(
names = {"--engine-jwt-disabled"},
description = "Disable authentication for Engine APIs (default: ${DEFAULT-VALUE})")
private final Boolean isEngineAuthDisabled = false;
@Option(
names = {"--engine-host-allowlist"},
paramLabel = "<hostname>[,<hostname>...]... or * or all",
description =
"Comma separated list of hostnames to allow for ENGINE API access (applies to both HTTP and websockets), or * to accept any host (default: ${DEFAULT-VALUE})",
defaultValue = "localhost,127.0.0.1")
private final JsonRPCAllowlistHostsProperty engineHostsAllowlist =
new JsonRPCAllowlistHostsProperty();
}
// JSON-RPC HTTP Options // JSON-RPC HTTP Options
@CommandLine.ArgGroup(validate = false, heading = "@|bold JSON-RPC HTTP Options|@%n") @CommandLine.ArgGroup(validate = false, heading = "@|bold JSON-RPC HTTP Options|@%n")
@ -1409,13 +1220,13 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
private Runner buildRunner() { private Runner buildRunner() {
return synchronize( return synchronize(
besuController, besuController,
p2PDiscoveryOptionGroup.p2pEnabled, p2PDiscoveryConfig.p2pEnabled(),
p2pTLSConfiguration, p2pTLSConfiguration,
p2PDiscoveryOptionGroup.peerDiscoveryEnabled, p2PDiscoveryConfig.peerDiscoveryEnabled(),
ethNetworkConfig, ethNetworkConfig,
p2PDiscoveryOptionGroup.p2pHost, p2PDiscoveryConfig.p2pHost(),
p2PDiscoveryOptionGroup.p2pInterface, p2PDiscoveryConfig.p2pInterface(),
p2PDiscoveryOptionGroup.p2pPort, p2PDiscoveryConfig.p2pPort(),
graphQLConfiguration, graphQLConfiguration,
jsonRpcConfiguration, jsonRpcConfiguration,
engineJsonRpcConfiguration, engineJsonRpcConfiguration,
@ -1614,7 +1425,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
private void validateOptions() { private void validateOptions() {
validateRequiredOptions(); validateRequiredOptions();
issueOptionWarnings(); issueOptionWarnings();
validateP2PInterface(p2PDiscoveryOptionGroup.p2pInterface); validateP2PInterface(p2PDiscoveryOptions.p2pInterface);
validateMiningParams(); validateMiningParams();
validateNatParams(); validateNatParams();
validateNetStatsParams(); validateNetStatsParams();
@ -1750,13 +1561,12 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
} }
private void ensureValidPeerBoundParams() { private void ensureValidPeerBoundParams() {
maxPeers = p2PDiscoveryOptionGroup.maxPeers; maxPeers = p2PDiscoveryOptions.maxPeers;
final Boolean isLimitRemoteWireConnectionsEnabled = final Boolean isLimitRemoteWireConnectionsEnabled =
p2PDiscoveryOptionGroup.isLimitRemoteWireConnectionsEnabled; p2PDiscoveryOptions.isLimitRemoteWireConnectionsEnabled;
if (isLimitRemoteWireConnectionsEnabled) { if (isLimitRemoteWireConnectionsEnabled) {
final float fraction = final float fraction =
Fraction.fromPercentage(p2PDiscoveryOptionGroup.maxRemoteConnectionsPercentage) Fraction.fromPercentage(p2PDiscoveryOptions.maxRemoteConnectionsPercentage).getValue();
.getValue();
checkState( checkState(
fraction >= 0.0 && fraction <= 1.0, fraction >= 0.0 && fraction <= 1.0,
"Fraction of remote connections allowed must be between 0.0 and 1.0 (inclusive)."); "Fraction of remote connections allowed must be between 0.0 and 1.0 (inclusive).");
@ -1820,7 +1630,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
logger, logger,
commandLine, commandLine,
"--p2p-enabled", "--p2p-enabled",
!p2PDiscoveryOptionGroup.p2pEnabled, !p2PDiscoveryOptions.p2pEnabled,
asList( asList(
"--bootnodes", "--bootnodes",
"--discovery-enabled", "--discovery-enabled",
@ -1861,6 +1671,8 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
} }
private void configure() throws Exception { private void configure() throws Exception {
p2PDiscoveryConfig = p2PDiscoveryOptions.toDomainObject();
engineRPCConfig = engineRPCOptions.toDomainObject();
checkPortClash(); checkPortClash();
checkIfRequiredPortsAreAvailable(); checkIfRequiredPortsAreAvailable();
syncMode = getDefaultSyncModeIfNotSet(); syncMode = getDefaultSyncModeIfNotSet();
@ -1870,25 +1682,18 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
jsonRpcConfiguration = jsonRpcConfiguration =
jsonRpcHttpOptions.jsonRpcConfiguration( jsonRpcHttpOptions.jsonRpcConfiguration(
hostsAllowlist, hostsAllowlist, p2PDiscoveryOptions.p2pHost, unstableRPCOptions.getHttpTimeoutSec());
p2PDiscoveryOptionGroup.autoDiscoverDefaultIP().getHostAddress(),
unstableRPCOptions.getHttpTimeoutSec());
if (isEngineApiEnabled()) { if (isEngineApiEnabled()) {
engineJsonRpcConfiguration = engineJsonRpcConfiguration = createEngineJsonRpcConfiguration();
createEngineJsonRpcConfiguration(
engineRPCOptionGroup.engineRpcPort, engineRPCOptionGroup.engineHostsAllowlist);
} }
p2pTLSConfiguration = p2pTLSConfigOptions.p2pTLSConfiguration(commandLine); p2pTLSConfiguration = p2pTLSConfigOptions.p2pTLSConfiguration(commandLine);
graphQLConfiguration = graphQLConfiguration =
graphQlOptions.graphQLConfiguration( graphQlOptions.graphQLConfiguration(
hostsAllowlist, hostsAllowlist, p2PDiscoveryOptions.p2pHost, unstableRPCOptions.getHttpTimeoutSec());
p2PDiscoveryOptionGroup.autoDiscoverDefaultIP().getHostAddress(),
unstableRPCOptions.getHttpTimeoutSec());
webSocketConfiguration = webSocketConfiguration =
rpcWebsocketOptions.webSocketConfiguration( rpcWebsocketOptions.webSocketConfiguration(
hostsAllowlist, hostsAllowlist, p2PDiscoveryConfig.p2pHost(), unstableRPCOptions.getWsTimeoutSec());
p2PDiscoveryOptionGroup.autoDiscoverDefaultIP().getHostAddress(),
unstableRPCOptions.getWsTimeoutSec());
jsonRpcIpcConfiguration = jsonRpcIpcConfiguration =
jsonRpcIpcConfiguration( jsonRpcIpcConfiguration(
unstableIpcOptions.isEnabled(), unstableIpcOptions.isEnabled(),
@ -2008,32 +1813,31 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
.requiredBlocks(requiredBlocks) .requiredBlocks(requiredBlocks)
.reorgLoggingThreshold(reorgLoggingThreshold) .reorgLoggingThreshold(reorgLoggingThreshold)
.evmConfiguration(unstableEvmOptions.toDomainObject()) .evmConfiguration(unstableEvmOptions.toDomainObject())
.maxPeers(p2PDiscoveryOptionGroup.maxPeers) .maxPeers(p2PDiscoveryOptions.maxPeers)
.maxRemotelyInitiatedPeers(maxRemoteInitiatedPeers) .maxRemotelyInitiatedPeers(maxRemoteInitiatedPeers)
.randomPeerPriority(p2PDiscoveryOptionGroup.randomPeerPriority) .randomPeerPriority(p2PDiscoveryOptions.randomPeerPriority)
.chainPruningConfiguration(unstableChainPruningOptions.toDomainObject()) .chainPruningConfiguration(unstableChainPruningOptions.toDomainObject())
.cacheLastBlocks(numberOfblocksToCache) .cacheLastBlocks(numberOfblocksToCache)
.genesisStateHashCacheEnabled(genesisStateHashCacheEnabled) .genesisStateHashCacheEnabled(genesisStateHashCacheEnabled)
.besuComponent(besuComponent); .besuComponent(besuComponent);
} }
private JsonRpcConfiguration createEngineJsonRpcConfiguration( private JsonRpcConfiguration createEngineJsonRpcConfiguration() {
final Integer engineListenPort, final List<String> allowCallsFrom) {
jsonRpcHttpOptions.checkDependencies(logger, commandLine); jsonRpcHttpOptions.checkDependencies(logger, commandLine);
final JsonRpcConfiguration engineConfig = final JsonRpcConfiguration engineConfig =
jsonRpcHttpOptions.jsonRpcConfiguration( jsonRpcHttpOptions.jsonRpcConfiguration(
allowCallsFrom, engineRPCConfig.engineHostsAllowlist(),
p2PDiscoveryOptionGroup.autoDiscoverDefaultIP().getHostAddress(), p2PDiscoveryConfig.p2pHost(),
unstableRPCOptions.getWsTimeoutSec()); unstableRPCOptions.getWsTimeoutSec());
engineConfig.setPort(engineListenPort); engineConfig.setPort(engineRPCConfig.engineRpcPort());
engineConfig.setRpcApis(Arrays.asList("ENGINE", "ETH")); engineConfig.setRpcApis(Arrays.asList("ENGINE", "ETH"));
engineConfig.setEnabled(isEngineApiEnabled()); engineConfig.setEnabled(isEngineApiEnabled());
if (!engineRPCOptionGroup.isEngineAuthDisabled) { if (!engineRPCConfig.isEngineAuthDisabled()) {
engineConfig.setAuthenticationEnabled(true); engineConfig.setAuthenticationEnabled(true);
engineConfig.setAuthenticationAlgorithm(JwtAlgorithm.HS256); engineConfig.setAuthenticationAlgorithm(JwtAlgorithm.HS256);
if (Objects.nonNull(engineRPCOptionGroup.engineJwtKeyFile) if (Objects.nonNull(engineRPCConfig.engineJwtKeyFile())
&& java.nio.file.Files.exists(engineRPCOptionGroup.engineJwtKeyFile)) { && java.nio.file.Files.exists(engineRPCConfig.engineJwtKeyFile())) {
engineConfig.setAuthenticationPublicKeyFile(engineRPCOptionGroup.engineJwtKeyFile.toFile()); engineConfig.setAuthenticationPublicKeyFile(engineRPCConfig.engineJwtKeyFile().toFile());
} else { } else {
logger.warn( logger.warn(
"Engine API authentication enabled without key file. Expect ephemeral jwt.hex file in datadir"); "Engine API authentication enabled without key file. Expect ephemeral jwt.hex file in datadir");
@ -2090,7 +1894,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
.enabled(metricsOptionGroup.getMetricsEnabled()) .enabled(metricsOptionGroup.getMetricsEnabled())
.host( .host(
Strings.isNullOrEmpty(metricsOptionGroup.getMetricsHost()) Strings.isNullOrEmpty(metricsOptionGroup.getMetricsHost())
? p2PDiscoveryOptionGroup.autoDiscoverDefaultIP().getHostAddress() ? p2PDiscoveryOptions.p2pHost
: metricsOptionGroup.getMetricsHost()) : metricsOptionGroup.getMetricsHost())
.port(metricsOptionGroup.getMetricsPort()) .port(metricsOptionGroup.getMetricsPort())
.protocol(metricsOptionGroup.getMetricsProtocol()) .protocol(metricsOptionGroup.getMetricsProtocol())
@ -2098,7 +1902,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
.pushEnabled(metricsOptionGroup.getMetricsPushEnabled()) .pushEnabled(metricsOptionGroup.getMetricsPushEnabled())
.pushHost( .pushHost(
Strings.isNullOrEmpty(metricsOptionGroup.getMetricsPushHost()) Strings.isNullOrEmpty(metricsOptionGroup.getMetricsPushHost())
? p2PDiscoveryOptionGroup.autoDiscoverDefaultIP().getHostAddress() ? p2PDiscoveryOptions.autoDiscoverDefaultIP().getHostAddress()
: metricsOptionGroup.getMetricsPushHost()) : metricsOptionGroup.getMetricsPushHost())
.pushPort(metricsOptionGroup.getMetricsPushPort()) .pushPort(metricsOptionGroup.getMetricsPushPort())
.pushInterval(metricsOptionGroup.getMetricsPushInterval()) .pushInterval(metricsOptionGroup.getMetricsPushInterval())
@ -2441,7 +2245,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
.apiConfiguration(apiConfiguration) .apiConfiguration(apiConfiguration)
.pidPath(pidPath) .pidPath(pidPath)
.dataDir(dataDir()) .dataDir(dataDir())
.bannedNodeIds(p2PDiscoveryOptionGroup.bannedNodeIds) .bannedNodeIds(p2PDiscoveryConfig.bannedNodeIds())
.metricsSystem((ObservableMetricsSystem) besuComponent.getMetricsSystem()) .metricsSystem((ObservableMetricsSystem) besuComponent.getMetricsSystem())
.permissioningService(permissioningService) .permissioningService(permissioningService)
.metricsConfiguration(metricsConfiguration) .metricsConfiguration(metricsConfiguration)
@ -2453,8 +2257,8 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
.storageProvider(keyValueStorageProvider(keyValueStorageName)) .storageProvider(keyValueStorageProvider(keyValueStorageName))
.rpcEndpointService(rpcEndpointServiceImpl) .rpcEndpointService(rpcEndpointServiceImpl)
.enodeDnsConfiguration(getEnodeDnsConfiguration()) .enodeDnsConfiguration(getEnodeDnsConfiguration())
.allowedSubnets(p2PDiscoveryOptionGroup.allowedSubnets) .allowedSubnets(p2PDiscoveryConfig.allowedSubnets())
.poaDiscoveryRetryBootnodes(p2PDiscoveryOptionGroup.poaDiscoveryRetryBootnodes) .poaDiscoveryRetryBootnodes(p2PDiscoveryConfig.poaDiscoveryRetryBootnodes())
.build(); .build();
addShutdownHook(runner); addShutdownHook(runner);
@ -2529,7 +2333,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
} }
} }
if (p2PDiscoveryOptionGroup.bootNodes == null) { if (p2PDiscoveryOptions.bootNodes == null) {
builder.setBootNodes(new ArrayList<>()); builder.setBootNodes(new ArrayList<>());
} }
builder.setDnsDiscoveryUrl(null); builder.setDnsDiscoveryUrl(null);
@ -2541,8 +2345,8 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
builder.setNetworkId(networkId); builder.setNetworkId(networkId);
} }
if (p2PDiscoveryOptionGroup.discoveryDnsUrl != null) { if (p2PDiscoveryOptions.discoveryDnsUrl != null) {
builder.setDnsDiscoveryUrl(p2PDiscoveryOptionGroup.discoveryDnsUrl); builder.setDnsDiscoveryUrl(p2PDiscoveryOptions.discoveryDnsUrl);
} else { } else {
final Optional<String> discoveryDnsUrlFromGenesis = final Optional<String> discoveryDnsUrlFromGenesis =
genesisConfigOptionsSupplier.get().getDiscoveryOptions().getDiscoveryDnsUrl(); genesisConfigOptionsSupplier.get().getDiscoveryOptions().getDiscoveryDnsUrl();
@ -2550,9 +2354,9 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
} }
List<EnodeURL> listBootNodes = null; List<EnodeURL> listBootNodes = null;
if (p2PDiscoveryOptionGroup.bootNodes != null) { if (p2PDiscoveryOptions.bootNodes != null) {
try { try {
listBootNodes = buildEnodes(p2PDiscoveryOptionGroup.bootNodes, getEnodeDnsConfiguration()); listBootNodes = buildEnodes(p2PDiscoveryOptions.bootNodes, getEnodeDnsConfiguration());
} catch (final IllegalArgumentException e) { } catch (final IllegalArgumentException e) {
throw new ParameterException(commandLine, e.getMessage()); throw new ParameterException(commandLine, e.getMessage());
} }
@ -2564,7 +2368,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
} }
} }
if (listBootNodes != null) { if (listBootNodes != null) {
if (!p2PDiscoveryOptionGroup.peerDiscoveryEnabled) { if (!p2PDiscoveryOptions.peerDiscoveryEnabled) {
logger.warn("Discovery disabled: bootnodes will be ignored."); logger.warn("Discovery disabled: bootnodes will be ignored.");
} }
DiscoveryConfiguration.assertValidBootnodes(listBootNodes); DiscoveryConfiguration.assertValidBootnodes(listBootNodes);
@ -2699,12 +2503,12 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
.filter(port -> port > 0) .filter(port -> port > 0)
.forEach( .forEach(
port -> { port -> {
if (port.equals(p2PDiscoveryOptionGroup.p2pPort) if (port.equals(p2PDiscoveryConfig.p2pPort())
&& (NetworkUtility.isPortUnavailableForTcp(port) && (NetworkUtility.isPortUnavailableForTcp(port)
|| NetworkUtility.isPortUnavailableForUdp(port))) { || NetworkUtility.isPortUnavailableForUdp(port))) {
unavailablePorts.add(port); unavailablePorts.add(port);
} }
if (!port.equals(p2PDiscoveryOptionGroup.p2pPort) if (!port.equals(p2PDiscoveryConfig.p2pPort())
&& NetworkUtility.isPortUnavailableForTcp(port)) { && NetworkUtility.isPortUnavailableForTcp(port)) {
unavailablePorts.add(port); unavailablePorts.add(port);
} }
@ -2724,15 +2528,14 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
*/ */
private List<Integer> getEffectivePorts() { private List<Integer> getEffectivePorts() {
final List<Integer> effectivePorts = new ArrayList<>(); final List<Integer> effectivePorts = new ArrayList<>();
addPortIfEnabled( addPortIfEnabled(effectivePorts, p2PDiscoveryOptions.p2pPort, p2PDiscoveryOptions.p2pEnabled);
effectivePorts, p2PDiscoveryOptionGroup.p2pPort, p2PDiscoveryOptionGroup.p2pEnabled);
addPortIfEnabled( addPortIfEnabled(
effectivePorts, graphQlOptions.getGraphQLHttpPort(), graphQlOptions.isGraphQLHttpEnabled()); effectivePorts, graphQlOptions.getGraphQLHttpPort(), graphQlOptions.isGraphQLHttpEnabled());
addPortIfEnabled( addPortIfEnabled(
effectivePorts, jsonRpcHttpOptions.getRpcHttpPort(), jsonRpcHttpOptions.isRpcHttpEnabled()); effectivePorts, jsonRpcHttpOptions.getRpcHttpPort(), jsonRpcHttpOptions.isRpcHttpEnabled());
addPortIfEnabled( addPortIfEnabled(
effectivePorts, rpcWebsocketOptions.getRpcWsPort(), rpcWebsocketOptions.isRpcWsEnabled()); effectivePorts, rpcWebsocketOptions.getRpcWsPort(), rpcWebsocketOptions.isRpcWsEnabled());
addPortIfEnabled(effectivePorts, engineRPCOptionGroup.engineRpcPort, isEngineApiEnabled()); addPortIfEnabled(effectivePorts, engineRPCConfig.engineRpcPort(), isEngineApiEnabled());
addPortIfEnabled( addPortIfEnabled(
effectivePorts, effectivePorts,
metricsOptionGroup.getMetricsPort(), metricsOptionGroup.getMetricsPort(),
@ -2859,7 +2662,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
} }
private boolean isEngineApiEnabled() { private boolean isEngineApiEnabled() {
return engineRPCOptionGroup.overrideEngineRpcEnabled || isMergeEnabled(); return engineRPCConfig.overrideEngineRpcEnabled() || isMergeEnabled();
} }
private SyncMode getDefaultSyncModeIfNotSet() { private SyncMode getDefaultSyncModeIfNotSet() {

@ -12,7 +12,7 @@
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
package org.hyperledger.besu.cli.options.stable; package org.hyperledger.besu.cli.options;
import static org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration.DEFAULT_BONSAI_LIMIT_TRIE_LOGS_ENABLED; import static org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration.DEFAULT_BONSAI_LIMIT_TRIE_LOGS_ENABLED;
import static org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration.DEFAULT_BONSAI_MAX_LAYERS_TO_LOAD; import static org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration.DEFAULT_BONSAI_MAX_LAYERS_TO_LOAD;
@ -22,7 +22,6 @@ import static org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration.
import static org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration.Unstable.DEFAULT_BONSAI_CODE_USING_CODE_HASH_ENABLED; import static org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration.Unstable.DEFAULT_BONSAI_CODE_USING_CODE_HASH_ENABLED;
import static org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration.Unstable.DEFAULT_BONSAI_FULL_FLAT_DB_ENABLED; import static org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration.Unstable.DEFAULT_BONSAI_FULL_FLAT_DB_ENABLED;
import org.hyperledger.besu.cli.options.CLIOptions;
import org.hyperledger.besu.cli.util.CommandLineUtils; import org.hyperledger.besu.cli.util.CommandLineUtils;
import org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration; import org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration;
import org.hyperledger.besu.ethereum.worldstate.ImmutableDataStorageConfiguration; import org.hyperledger.besu.ethereum.worldstate.ImmutableDataStorageConfiguration;

@ -28,6 +28,7 @@ import picocli.CommandLine;
* Handles configuration options for the API in Besu, including gas price settings, RPC log range, * Handles configuration options for the API in Besu, including gas price settings, RPC log range,
* and trace filter range. * and trace filter range.
*/ */
// TODO: implement CLIOption<ApiConfiguration>
public class ApiConfigurationOptions { public class ApiConfigurationOptions {
/** Default constructor. */ /** Default constructor. */
public ApiConfigurationOptions() {} public ApiConfigurationOptions() {}

@ -0,0 +1,36 @@
/*
* Copyright contributors to Hyperledger Besu.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.cli.options.stable;
import org.hyperledger.besu.cli.custom.JsonRPCAllowlistHostsProperty;
import java.nio.file.Path;
/**
* Command line options for configuring Engine RPC on the node.
*
* @param overrideEngineRpcEnabled enable the engine api, even in the absence of merge-specific
* configurations.
* @param engineRpcPort Port to provide consensus client APIS on
* @param engineJwtKeyFile Path to file containing shared secret key for JWT signature verification
* @param isEngineAuthDisabled Disable authentication for Engine APIs
* @param engineHostsAllowlist List of hosts to allowlist for Engine APIs
*/
public record EngineRPCConfiguration(
Boolean overrideEngineRpcEnabled,
Integer engineRpcPort,
Path engineJwtKeyFile,
Boolean isEngineAuthDisabled,
JsonRPCAllowlistHostsProperty engineHostsAllowlist) {}

@ -0,0 +1,81 @@
/*
* Copyright contributors to Hyperledger Besu.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.cli.options.stable;
import static org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcConfiguration.DEFAULT_ENGINE_JSON_RPC_PORT;
import org.hyperledger.besu.cli.DefaultCommandValues;
import org.hyperledger.besu.cli.custom.JsonRPCAllowlistHostsProperty;
import org.hyperledger.besu.cli.options.CLIOptions;
import org.hyperledger.besu.cli.util.CommandLineUtils;
import java.nio.file.Path;
import java.util.List;
import picocli.CommandLine;
/** Command line options for configuring Engine RPC on the node. */
public class EngineRPCOptions implements CLIOptions<EngineRPCConfiguration> {
/** Default constructor */
public EngineRPCOptions() {}
@CommandLine.Option(
names = {"--engine-rpc-enabled"},
description = "enable the engine api, even in the absence of merge-specific configurations.")
private final Boolean overrideEngineRpcEnabled = false;
@CommandLine.Option(
names = {"--engine-rpc-port", "--engine-rpc-http-port"},
paramLabel = DefaultCommandValues.MANDATORY_PORT_FORMAT_HELP,
description = "Port to provide consensus client APIS on (default: ${DEFAULT-VALUE})",
arity = "1")
private final Integer engineRpcPort = DEFAULT_ENGINE_JSON_RPC_PORT;
@CommandLine.Option(
names = {"--engine-jwt-secret"},
paramLabel = DefaultCommandValues.MANDATORY_FILE_FORMAT_HELP,
description = "Path to file containing shared secret key for JWT signature verification")
private final Path engineJwtKeyFile = null;
@CommandLine.Option(
names = {"--engine-jwt-disabled"},
description = "Disable authentication for Engine APIs (default: ${DEFAULT-VALUE})")
private final Boolean isEngineAuthDisabled = false;
@CommandLine.Option(
names = {"--engine-host-allowlist"},
paramLabel = "<hostname>[,<hostname>...]... or * or all",
description =
"Comma separated list of hostnames to allow for ENGINE API access (applies to both HTTP and websockets), or * to accept any host (default: ${DEFAULT-VALUE})",
defaultValue = "localhost,127.0.0.1")
private final JsonRPCAllowlistHostsProperty engineHostsAllowlist =
new JsonRPCAllowlistHostsProperty();
@Override
public EngineRPCConfiguration toDomainObject() {
return new EngineRPCConfiguration(
overrideEngineRpcEnabled,
engineRpcPort,
engineJwtKeyFile,
isEngineAuthDisabled,
engineHostsAllowlist);
}
@Override
public List<String> getCLIOptions() {
return CommandLineUtils.getCLIOptions(this, new EngineRPCOptions());
}
}

@ -29,6 +29,7 @@ import org.slf4j.Logger;
import picocli.CommandLine; import picocli.CommandLine;
/** Handles configuration options for the GraphQL HTTP service in Besu. */ /** Handles configuration options for the GraphQL HTTP service in Besu. */
// TODO: implement CLIOptions<GraphQLConfiguration>
public class GraphQlOptions { public class GraphQlOptions {
@CommandLine.Option( @CommandLine.Option(
names = {"--graphql-http-enabled"}, names = {"--graphql-http-enabled"},

@ -52,6 +52,7 @@ import picocli.CommandLine;
* Handles configuration options for the JSON-RPC HTTP service, including validation and creation of * Handles configuration options for the JSON-RPC HTTP service, including validation and creation of
* a JSON-RPC configuration. * a JSON-RPC configuration.
*/ */
// TODO: implement CLIOption<JsonRpcConfiguration>
public class JsonRpcHttpOptions { public class JsonRpcHttpOptions {
@CommandLine.Option( @CommandLine.Option(
names = {"--rpc-http-enabled"}, names = {"--rpc-http-enabled"},
@ -265,37 +266,50 @@ public class JsonRpcHttpOptions {
/** /**
* Creates a JsonRpcConfiguration based on the provided options. * Creates a JsonRpcConfiguration based on the provided options.
* *
* @param hostsAllowlist List of hosts allowed * @return configuration populated from options or defaults
* @param defaultHostAddress Default host address
* @param timoutSec timeout in seconds
* @return A JsonRpcConfiguration instance
*/ */
public JsonRpcConfiguration jsonRpcConfiguration( public JsonRpcConfiguration jsonRpcConfiguration() {
final List<String> hostsAllowlist, final String defaultHostAddress, final Long timoutSec) {
final JsonRpcConfiguration jsonRpcConfiguration = JsonRpcConfiguration.createDefault(); final JsonRpcConfiguration jsonRpcConfiguration = JsonRpcConfiguration.createDefault();
jsonRpcConfiguration.setEnabled(isRpcHttpEnabled); jsonRpcConfiguration.setEnabled(isRpcHttpEnabled);
jsonRpcConfiguration.setHost(
Strings.isNullOrEmpty(rpcHttpHost) ? defaultHostAddress : rpcHttpHost);
jsonRpcConfiguration.setPort(rpcHttpPort); jsonRpcConfiguration.setPort(rpcHttpPort);
jsonRpcConfiguration.setMaxActiveConnections(rpcHttpMaxConnections); jsonRpcConfiguration.setMaxActiveConnections(rpcHttpMaxConnections);
jsonRpcConfiguration.setCorsAllowedDomains(rpcHttpCorsAllowedOrigins); jsonRpcConfiguration.setCorsAllowedDomains(rpcHttpCorsAllowedOrigins);
jsonRpcConfiguration.setRpcApis(rpcHttpApis.stream().distinct().collect(Collectors.toList())); jsonRpcConfiguration.setRpcApis(rpcHttpApis.stream().distinct().collect(Collectors.toList()));
jsonRpcConfiguration.setNoAuthRpcApis( jsonRpcConfiguration.setNoAuthRpcApis(
rpcHttpApiMethodsNoAuth.stream().distinct().collect(Collectors.toList())); rpcHttpApiMethodsNoAuth.stream().distinct().collect(Collectors.toList()));
jsonRpcConfiguration.setHostsAllowlist(hostsAllowlist);
jsonRpcConfiguration.setAuthenticationEnabled(isRpcHttpAuthenticationEnabled); jsonRpcConfiguration.setAuthenticationEnabled(isRpcHttpAuthenticationEnabled);
jsonRpcConfiguration.setAuthenticationCredentialsFile(rpcHttpAuthenticationCredentialsFile); jsonRpcConfiguration.setAuthenticationCredentialsFile(rpcHttpAuthenticationCredentialsFile);
jsonRpcConfiguration.setAuthenticationPublicKeyFile(rpcHttpAuthenticationPublicKeyFile); jsonRpcConfiguration.setAuthenticationPublicKeyFile(rpcHttpAuthenticationPublicKeyFile);
jsonRpcConfiguration.setAuthenticationAlgorithm(rpcHttpAuthenticationAlgorithm); jsonRpcConfiguration.setAuthenticationAlgorithm(rpcHttpAuthenticationAlgorithm);
jsonRpcConfiguration.setTlsConfiguration(rpcHttpTlsConfiguration()); jsonRpcConfiguration.setTlsConfiguration(rpcHttpTlsConfiguration());
jsonRpcConfiguration.setHttpTimeoutSec(timoutSec);
jsonRpcConfiguration.setMaxBatchSize(rpcHttpMaxBatchSize); jsonRpcConfiguration.setMaxBatchSize(rpcHttpMaxBatchSize);
jsonRpcConfiguration.setMaxRequestContentLength(rpcHttpMaxRequestContentLength); jsonRpcConfiguration.setMaxRequestContentLength(rpcHttpMaxRequestContentLength);
jsonRpcConfiguration.setPrettyJsonEnabled(prettyJsonEnabled); jsonRpcConfiguration.setPrettyJsonEnabled(prettyJsonEnabled);
return jsonRpcConfiguration; return jsonRpcConfiguration;
} }
/**
* Creates a JsonRpcConfiguration based on the provided options.
*
* @param hostsAllowlist List of hosts allowed
* @param defaultHostAddress Default host address
* @param timoutSec timeout in seconds
* @return A JsonRpcConfiguration instance
*/
public JsonRpcConfiguration jsonRpcConfiguration(
final List<String> hostsAllowlist, final String defaultHostAddress, final Long timoutSec) {
final JsonRpcConfiguration jsonRpcConfiguration = this.jsonRpcConfiguration();
jsonRpcConfiguration.setHost(
Strings.isNullOrEmpty(rpcHttpHost) ? defaultHostAddress : rpcHttpHost);
jsonRpcConfiguration.setHostsAllowlist(hostsAllowlist);
;
jsonRpcConfiguration.setHttpTimeoutSec(timoutSec);
return jsonRpcConfiguration;
}
/** /**
* Checks dependencies between options. * Checks dependencies between options.
* *

@ -30,6 +30,7 @@ import java.util.Set;
import picocli.CommandLine; import picocli.CommandLine;
/** Command line options for configuring metrics. */ /** Command line options for configuring metrics. */
// TODO: implement CLIOption<MetricsConfiguration> and rename to drop the Group
public class MetricsOptionGroup { public class MetricsOptionGroup {
@CommandLine.Option( @CommandLine.Option(
names = {"--metrics-enabled"}, names = {"--metrics-enabled"},

@ -0,0 +1,238 @@
/*
* Copyright contributors to Hyperledger Besu.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.cli.options.stable;
import org.hyperledger.besu.cli.DefaultCommandValues;
import org.hyperledger.besu.cli.converter.PercentageConverter;
import org.hyperledger.besu.cli.converter.SubnetInfoConverter;
import org.hyperledger.besu.cli.options.CLIOptions;
import org.hyperledger.besu.cli.util.CommandLineUtils;
import org.hyperledger.besu.ethereum.p2p.discovery.P2PDiscoveryConfiguration;
import org.hyperledger.besu.ethereum.p2p.peers.EnodeURLImpl;
import org.hyperledger.besu.util.NetworkUtility;
import org.hyperledger.besu.util.number.Fraction;
import org.hyperledger.besu.util.number.Percentage;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import org.apache.commons.net.util.SubnetUtils;
import org.apache.tuweni.bytes.Bytes;
import picocli.CommandLine;
/** Command line options for configuring P2P discovery on the node. */
public class P2PDiscoveryOptions implements CLIOptions<P2PDiscoveryConfiguration> {
/** Default constructor */
public P2PDiscoveryOptions() {}
// Public IP stored to prevent having to research it each time we need it.
private InetAddress autoDiscoveredDefaultIP = null;
/** Completely disables P2P within Besu. */
@CommandLine.Option(
names = {"--p2p-enabled"},
description = "Enable P2P functionality (default: ${DEFAULT-VALUE})",
arity = "1")
public final Boolean p2pEnabled = true;
/**
* Boolean option to indicate if peers should NOT be discovered, default to false indicates that
* the peers should be discovered by default.
*/
//
// This negative option is required because of the nature of the option that is
// true when
// added on the command line. You can't do --option=false, so false is set as
// default
// and you have not to set the option at all if you want it false.
// This seems to be the only way it works with Picocli.
// Also many other software use the same negative option scheme for false
// defaults
// meaning that it's probably the right way to handle disabling options.
@CommandLine.Option(
names = {"--discovery-enabled"},
description = "Enable P2P discovery (default: ${DEFAULT-VALUE})",
arity = "1")
public final Boolean peerDiscoveryEnabled = true;
/**
* A list of bootstrap nodes can be passed and a hardcoded list will be used otherwise by the
* Runner.
*/
// NOTE: we have no control over default value here.
@CommandLine.Option(
names = {"--bootnodes"},
paramLabel = "<enode://id@host:port>",
description =
"Comma separated enode URLs for P2P discovery bootstrap. "
+ "Default is a predefined list.",
split = ",",
arity = "0..*")
public final List<String> bootNodes = null;
/** The IP the node advertises to peers for P2P communication. */
@SuppressWarnings({"FieldCanBeFinal", "FieldMayBeFinal"}) // PicoCLI requires non-final Strings.
@CommandLine.Option(
names = {"--p2p-host"},
paramLabel = DefaultCommandValues.MANDATORY_HOST_FORMAT_HELP,
description = "IP address this node advertises to its peers (default: ${DEFAULT-VALUE})",
arity = "1")
public String p2pHost = autoDiscoverDefaultIP().getHostAddress();
/** The network interface address on which this node listens for P2P communication. */
@SuppressWarnings({"FieldCanBeFinal", "FieldMayBeFinal"}) // PicoCLI requires non-final Strings.
@CommandLine.Option(
names = {"--p2p-interface"},
paramLabel = DefaultCommandValues.MANDATORY_HOST_FORMAT_HELP,
description =
"The network interface address on which this node listens for P2P communication (default: ${DEFAULT-VALUE})",
arity = "1")
public String p2pInterface = NetworkUtility.INADDR_ANY;
/** The port on which this node listens for P2P communication. */
@CommandLine.Option(
names = {"--p2p-port"},
paramLabel = DefaultCommandValues.MANDATORY_PORT_FORMAT_HELP,
description = "Port on which to listen for P2P communication (default: ${DEFAULT-VALUE})",
arity = "1")
public final Integer p2pPort = EnodeURLImpl.DEFAULT_LISTENING_PORT;
/** The maximum number of peers this node can connect to. */
@CommandLine.Option(
names = {"--max-peers", "--p2p-peer-upper-bound"},
paramLabel = DefaultCommandValues.MANDATORY_INTEGER_FORMAT_HELP,
description = "Maximum P2P connections that can be established (default: ${DEFAULT-VALUE})")
public final Integer maxPeers = DefaultCommandValues.DEFAULT_MAX_PEERS;
/** Boolean option to limit the number of P2P connections initiated remotely. */
@CommandLine.Option(
names = {"--remote-connections-limit-enabled"},
description =
"Whether to limit the number of P2P connections initiated remotely. (default: ${DEFAULT-VALUE})")
public final Boolean isLimitRemoteWireConnectionsEnabled = true;
/** The maximum percentage of P2P connections that can be initiated remotely. */
@CommandLine.Option(
names = {"--remote-connections-max-percentage"},
paramLabel = DefaultCommandValues.MANDATORY_DOUBLE_FORMAT_HELP,
description =
"The maximum percentage of P2P connections that can be initiated remotely. Must be between 0 and 100 inclusive. (default: ${DEFAULT-VALUE})",
arity = "1",
converter = PercentageConverter.class)
public final Percentage maxRemoteConnectionsPercentage =
Fraction.fromFloat(DefaultCommandValues.DEFAULT_FRACTION_REMOTE_WIRE_CONNECTIONS_ALLOWED)
.toPercentage();
/** The URL to use for DNS discovery. */
@SuppressWarnings({"FieldCanBeFinal", "FieldMayBeFinal"}) // PicoCLI requires non-final Strings.
@CommandLine.Option(
names = {"--discovery-dns-url"},
description = "Specifies the URL to use for DNS discovery")
public String discoveryDnsUrl = null;
/** Boolean option to allow for incoming connections to be prioritized randomly. */
@CommandLine.Option(
names = {"--random-peer-priority-enabled"},
description =
"Allow for incoming connections to be prioritized randomly. This will prevent (typically small, stable) networks from forming impenetrable peer cliques. (default: ${DEFAULT-VALUE})")
public final Boolean randomPeerPriority = Boolean.FALSE;
/** A list of node IDs to ban from the P2P network. */
@CommandLine.Option(
names = {"--banned-node-ids", "--banned-node-id"},
paramLabel = DefaultCommandValues.MANDATORY_NODE_ID_FORMAT_HELP,
description = "A list of node IDs to ban from the P2P network.",
split = ",",
arity = "1..*")
void setBannedNodeIds(final List<String> values) {
try {
bannedNodeIds =
values.stream()
.filter(value -> !value.isEmpty())
.map(EnodeURLImpl::parseNodeId)
.collect(Collectors.toList());
} catch (final IllegalArgumentException e) {
throw new CommandLine.ParameterException(
new CommandLine(this), "Invalid ids supplied to '--banned-node-ids'. " + e.getMessage());
}
}
// Boolean option to set that in a PoA network the bootnodes should always be queried during
// peer table refresh. If this flag is disabled bootnodes are only sent FINDN requests on first
// startup, meaning that an offline bootnode or network outage at the client can prevent it
// discovering any peers without a restart.
@CommandLine.Option(
names = {"--poa-discovery-retry-bootnodes"},
description =
"Always use of bootnodes for discovery in PoA networks. Disabling this reverts "
+ " to the same behaviour as non-PoA networks, where neighbours are only discovered from bootnodes on first startup."
+ "(default: ${DEFAULT-VALUE})",
arity = "1")
private final Boolean poaDiscoveryRetryBootnodes = true;
private Collection<Bytes> bannedNodeIds = new ArrayList<>();
/**
* Auto-discovers the default IP of the client.
*
* @return machine loopback address
*/
// Loopback IP is used by default as this is how smokeTests require it to be
// and it's probably a good security behaviour to default only on the localhost.
public InetAddress autoDiscoverDefaultIP() {
autoDiscoveredDefaultIP =
Optional.ofNullable(autoDiscoveredDefaultIP).orElseGet(InetAddress::getLoopbackAddress);
return autoDiscoveredDefaultIP;
}
@CommandLine.Option(
names = {"--net-restrict"},
arity = "1..*",
split = ",",
converter = SubnetInfoConverter.class,
description =
"Comma-separated list of allowed IP subnets (e.g., '192.168.1.0/24,10.0.0.0/8').")
private List<SubnetUtils.SubnetInfo> allowedSubnets;
@Override
public P2PDiscoveryConfiguration toDomainObject() {
return new P2PDiscoveryConfiguration(
p2pEnabled,
peerDiscoveryEnabled,
p2pHost,
p2pInterface,
p2pPort,
maxPeers,
isLimitRemoteWireConnectionsEnabled,
maxRemoteConnectionsPercentage,
randomPeerPriority,
bannedNodeIds,
allowedSubnets,
poaDiscoveryRetryBootnodes,
bootNodes,
discoveryDnsUrl);
}
@Override
public List<String> getCLIOptions() {
return CommandLineUtils.getCLIOptions(this, new P2PDiscoveryOptions());
}
}

@ -30,6 +30,7 @@ import org.slf4j.Logger;
import picocli.CommandLine; import picocli.CommandLine;
/** Handles configuration options for permissions in Besu. */ /** Handles configuration options for permissions in Besu. */
// TODO: implement CLIOption<PermissioningConfiguration>
public class PermissionsOptions { public class PermissionsOptions {
@CommandLine.Option( @CommandLine.Option(
names = {"--permissions-nodes-config-file-enabled"}, names = {"--permissions-nodes-config-file-enabled"},

@ -23,6 +23,7 @@ import java.util.List;
import picocli.CommandLine; import picocli.CommandLine;
/** The Metrics cli options. */ /** The Metrics cli options. */
// TODO: combine into MetricsOptionGroup, use Unstable inner class pattern (see MiningOptions)
public class MetricsCLIOptions implements CLIOptions<MetricsConfiguration.Builder> { public class MetricsCLIOptions implements CLIOptions<MetricsConfiguration.Builder> {
private static final String TIMERS_ENABLED_FLAG = "--Xmetrics-timers-enabled"; private static final String TIMERS_ENABLED_FLAG = "--Xmetrics-timers-enabled";
private static final String IDLE_TIMEOUT_FLAG = "--Xmetrics-idle-timeout"; private static final String IDLE_TIMEOUT_FLAG = "--Xmetrics-idle-timeout";

@ -12,7 +12,7 @@
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
package org.hyperledger.besu.cli.options.stable; package org.hyperledger.besu.cli.options.unstable;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import static org.hyperledger.besu.cli.DefaultCommandValues.DEFAULT_KEYSTORE_TYPE; import static org.hyperledger.besu.cli.DefaultCommandValues.DEFAULT_KEYSTORE_TYPE;

@ -15,11 +15,11 @@
package org.hyperledger.besu.cli.subcommands.storage; package org.hyperledger.besu.cli.subcommands.storage;
import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkArgument;
import static org.hyperledger.besu.cli.options.stable.DataStorageOptions.BONSAI_STORAGE_FORMAT_MAX_LAYERS_TO_LOAD; import static org.hyperledger.besu.cli.options.DataStorageOptions.BONSAI_STORAGE_FORMAT_MAX_LAYERS_TO_LOAD;
import static org.hyperledger.besu.controller.BesuController.DATABASE_PATH; import static org.hyperledger.besu.controller.BesuController.DATABASE_PATH;
import static org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration.DEFAULT_BONSAI_TRIE_LOG_PRUNING_WINDOW_SIZE; import static org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration.DEFAULT_BONSAI_TRIE_LOG_PRUNING_WINDOW_SIZE;
import org.hyperledger.besu.cli.options.stable.DataStorageOptions; import org.hyperledger.besu.cli.options.DataStorageOptions;
import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.ethereum.chain.Blockchain; import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.chain.MutableBlockchain; import org.hyperledger.besu.ethereum.chain.MutableBlockchain;

@ -20,7 +20,10 @@ import org.hyperledger.besu.chainexport.RlpBlockExporter;
import org.hyperledger.besu.chainimport.JsonBlockImporter; import org.hyperledger.besu.chainimport.JsonBlockImporter;
import org.hyperledger.besu.chainimport.RlpBlockImporter; import org.hyperledger.besu.chainimport.RlpBlockImporter;
import org.hyperledger.besu.cli.BesuCommand; import org.hyperledger.besu.cli.BesuCommand;
import org.hyperledger.besu.cli.options.stable.P2PDiscoveryOptions;
import org.hyperledger.besu.cli.options.unstable.RPCOptions;
import org.hyperledger.besu.controller.BesuController; import org.hyperledger.besu.controller.BesuController;
import org.hyperledger.besu.ethereum.p2p.discovery.P2PDiscoveryConfiguration;
import org.hyperledger.besu.metrics.prometheus.MetricsConfiguration; import org.hyperledger.besu.metrics.prometheus.MetricsConfiguration;
import org.hyperledger.besu.services.BesuPluginContextImpl; import org.hyperledger.besu.services.BesuPluginContextImpl;
@ -53,7 +56,6 @@ public class BesuCommandModule {
new BesuPluginContextImpl(), new BesuPluginContextImpl(),
System.getenv(), System.getenv(),
commandLogger); commandLogger);
besuCommand.toCommandLine();
return besuCommand; return besuCommand;
} }
@ -63,6 +65,18 @@ public class BesuCommandModule {
return provideFrom.metricsConfiguration(); return provideFrom.metricsConfiguration();
} }
@Provides
@Singleton
RPCOptions provideRPCOptions() {
return RPCOptions.create();
}
@Provides
@Singleton
P2PDiscoveryConfiguration provideP2PDiscoveryConfiguration() {
return new P2PDiscoveryOptions().toDomainObject();
}
@Provides @Provides
@Named("besuCommandLogger") @Named("besuCommandLogger")
@Singleton @Singleton

@ -30,6 +30,7 @@ import org.hyperledger.besu.plugin.data.BlockHeader;
import org.hyperledger.besu.plugin.data.TransactionReceipt; import org.hyperledger.besu.plugin.data.TransactionReceipt;
import org.hyperledger.besu.plugin.services.BlockchainService; import org.hyperledger.besu.plugin.services.BlockchainService;
import java.math.BigInteger;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.function.Supplier; import java.util.function.Supplier;
@ -182,4 +183,12 @@ public class BlockchainServiceImpl implements BlockchainService {
} }
}; };
} }
@Override
public Optional<BigInteger> getChainId() {
if (protocolSchedule == null) {
return Optional.empty();
}
return protocolSchedule.getChainId();
}
} }

@ -34,9 +34,9 @@ import org.hyperledger.besu.chainexport.RlpBlockExporter;
import org.hyperledger.besu.chainimport.JsonBlockImporter; import org.hyperledger.besu.chainimport.JsonBlockImporter;
import org.hyperledger.besu.chainimport.RlpBlockImporter; import org.hyperledger.besu.chainimport.RlpBlockImporter;
import org.hyperledger.besu.cli.config.EthNetworkConfig; import org.hyperledger.besu.cli.config.EthNetworkConfig;
import org.hyperledger.besu.cli.options.DataStorageOptions;
import org.hyperledger.besu.cli.options.MiningOptions; import org.hyperledger.besu.cli.options.MiningOptions;
import org.hyperledger.besu.cli.options.TransactionPoolOptions; import org.hyperledger.besu.cli.options.TransactionPoolOptions;
import org.hyperledger.besu.cli.options.stable.DataStorageOptions;
import org.hyperledger.besu.cli.options.stable.EthstatsOptions; import org.hyperledger.besu.cli.options.stable.EthstatsOptions;
import org.hyperledger.besu.cli.options.unstable.EthProtocolOptions; import org.hyperledger.besu.cli.options.unstable.EthProtocolOptions;
import org.hyperledger.besu.cli.options.unstable.MetricsCLIOptions; import org.hyperledger.besu.cli.options.unstable.MetricsCLIOptions;

@ -18,6 +18,7 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration.MINIMUM_BONSAI_TRIE_LOG_RETENTION_LIMIT; import static org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration.MINIMUM_BONSAI_TRIE_LOG_RETENTION_LIMIT;
import org.hyperledger.besu.cli.options.AbstractCLIOptionsTest; import org.hyperledger.besu.cli.options.AbstractCLIOptionsTest;
import org.hyperledger.besu.cli.options.DataStorageOptions;
import org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration; import org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration;
import org.hyperledger.besu.ethereum.worldstate.ImmutableDataStorageConfiguration; import org.hyperledger.besu.ethereum.worldstate.ImmutableDataStorageConfiguration;
import org.hyperledger.besu.plugin.services.storage.DataStorageFormat; import org.hyperledger.besu.plugin.services.storage.DataStorageFormat;

@ -690,7 +690,7 @@ startScripts {
"-XX:G1ConcRefinementThreads=2", "-XX:G1ConcRefinementThreads=2",
"-XX:G1HeapWastePercent=15", "-XX:G1HeapWastePercent=15",
"-XX:MaxGCPauseMillis=100", "-XX:MaxGCPauseMillis=100",
"-XX:StartFlightRecording,dumponexit=true,settings=default.jfc", "-XX:StartFlightRecording,settings=default.jfc",
"-Xlog:jfr*=off" "-Xlog:jfr*=off"
] ]
unixStartScriptGenerator.template = resources.text.fromFile("${projectDir}/besu/src/main/scripts/unixStartScript.txt") unixStartScriptGenerator.template = resources.text.fromFile("${projectDir}/besu/src/main/scripts/unixStartScript.txt")

@ -17,6 +17,8 @@ package org.hyperledger.besu.config;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
/** The Merge config options. */ /** The Merge config options. */
// TODO: naming this with Options as the suffix is misleading, it should be MergeConfig - doesn't
// use picocli
public class MergeConfigOptions { public class MergeConfigOptions {
private static final AtomicBoolean mergeEnabled = new AtomicBoolean(false); private static final AtomicBoolean mergeEnabled = new AtomicBoolean(false);

@ -26,6 +26,7 @@ import org.hyperledger.besu.ethereum.api.query.TransactionWithMetadata;
import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.LogWithMetadata; import org.hyperledger.besu.ethereum.core.LogWithMetadata;
import org.hyperledger.besu.ethereum.core.Transaction; import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.TransactionReceipt;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput; import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
@ -292,6 +293,23 @@ public class TransactionAdapter extends AdapterBase {
: Optional.of((long) receipt.getStatus())); : Optional.of((long) receipt.getStatus()));
} }
/**
* Retrieves the revert reason of the transaction, if any.
*
* <p>This method uses the getReceipt method to get the receipt of the transaction. It then checks
* the revert reason of the receipt. It would be an empty Optional for successful transactions.
* Otherwise, it returns an Optional containing the revert reason.
*
* @param environment the data fetching environment.
* @return an Optional containing a Bytes object representing the revert reason of the
* transaction, or an empty Optional .
*/
public Optional<Bytes> getRevertReason(final DataFetchingEnvironment environment) {
return getReceipt(environment)
.map(TransactionReceiptWithMetadata::getReceipt)
.flatMap(TransactionReceipt::getRevertReason);
}
/** /**
* Retrieves the gas used by the transaction. * Retrieves the gas used by the transaction.
* *

@ -579,6 +579,9 @@ type Transaction {
BlobVersionedHashes is a set of hash outputs from the blobs in the transaction. BlobVersionedHashes is a set of hash outputs from the blobs in the transaction.
""" """
blobVersionedHashes: [Bytes32!] blobVersionedHashes: [Bytes32!]
"""Reason returned when transaction fails."""
revertReason: Bytes
} }
"""EIP-4895""" """EIP-4895"""

@ -230,16 +230,9 @@ public class TransactionSimulator {
final Account sender = updater.get(senderAddress); final Account sender = updater.get(senderAddress);
final long nonce = sender != null ? sender.getNonce() : 0L; final long nonce = sender != null ? sender.getNonce() : 0L;
long gasLimit = final long simulationGasCap =
callParams.getGasLimit() >= 0 calculateSimulationGasCap(callParams.getGasLimit(), blockHeaderToProcess.getGasLimit());
? callParams.getGasLimit()
: blockHeaderToProcess.getGasLimit();
if (rpcGasCap > 0) {
gasLimit = rpcGasCap;
LOG.trace(
"Gas limit capped at {} for transaction simulation due to provided RPC gas cap.",
rpcGasCap);
}
final Wei value = callParams.getValue() != null ? callParams.getValue() : Wei.ZERO; final Wei value = callParams.getValue() != null ? callParams.getValue() : Wei.ZERO;
final Bytes payload = callParams.getPayload() != null ? callParams.getPayload() : Bytes.EMPTY; final Bytes payload = callParams.getPayload() != null ? callParams.getPayload() : Bytes.EMPTY;
@ -265,7 +258,7 @@ public class TransactionSimulator {
header, header,
senderAddress, senderAddress,
nonce, nonce,
gasLimit, simulationGasCap,
value, value,
payload, payload,
blobGasPrice); blobGasPrice);
@ -291,6 +284,38 @@ public class TransactionSimulator {
return Optional.of(new TransactionSimulatorResult(transaction, result)); return Optional.of(new TransactionSimulatorResult(transaction, result));
} }
private long calculateSimulationGasCap(
final long userProvidedGasLimit, final long blockGasLimit) {
final long simulationGasCap;
// when not set gas limit is -1
if (userProvidedGasLimit >= 0) {
if (rpcGasCap > 0 && userProvidedGasLimit > rpcGasCap) {
LOG.trace(
"User provided gas limit {} is bigger than the value of rpc-gas-cap {}, setting simulation gas cap to the latter",
userProvidedGasLimit,
rpcGasCap);
simulationGasCap = rpcGasCap;
} else {
LOG.trace("Using provided gas limit {} set as simulation gas cap", userProvidedGasLimit);
simulationGasCap = userProvidedGasLimit;
}
} else {
if (rpcGasCap > 0) {
LOG.trace(
"No user provided gas limit, setting simulation gas cap to the value of rpc-gas-cap {}",
rpcGasCap);
simulationGasCap = rpcGasCap;
} else {
simulationGasCap = blockGasLimit;
LOG.trace(
"No user provided gas limit and rpc-gas-cap options is not set, setting simulation gas cap to block gas limit {}",
blockGasLimit);
}
}
return simulationGasCap;
}
private Optional<Transaction> buildTransaction( private Optional<Transaction> buildTransaction(
final CallParameter callParams, final CallParameter callParams,
final TransactionValidationParams transactionValidationParams, final TransactionValidationParams transactionValidationParams,

@ -79,7 +79,8 @@ public class TransactionSimulatorTest {
private static final Address DEFAULT_FROM = private static final Address DEFAULT_FROM =
Address.fromHexString("0x0000000000000000000000000000000000000000"); Address.fromHexString("0x0000000000000000000000000000000000000000");
private static final long GASCAP = 500L; private static final long GAS_CAP = 500000L;
private static final long TRANSFER_GAS_LIMIT = 21000L;
private TransactionSimulator transactionSimulator; private TransactionSimulator transactionSimulator;
private TransactionSimulator cappedTransactionSimulator; private TransactionSimulator cappedTransactionSimulator;
@ -96,7 +97,7 @@ public class TransactionSimulatorTest {
this.transactionSimulator = this.transactionSimulator =
new TransactionSimulator(blockchain, worldStateArchive, protocolSchedule, 0); new TransactionSimulator(blockchain, worldStateArchive, protocolSchedule, 0);
this.cappedTransactionSimulator = this.cappedTransactionSimulator =
new TransactionSimulator(blockchain, worldStateArchive, protocolSchedule, GASCAP); new TransactionSimulator(blockchain, worldStateArchive, protocolSchedule, GAS_CAP);
} }
@Test @Test
@ -124,7 +125,7 @@ public class TransactionSimulatorTest {
.type(TransactionType.FRONTIER) .type(TransactionType.FRONTIER)
.nonce(1L) .nonce(1L)
.gasPrice(callParameter.getGasPrice()) .gasPrice(callParameter.getGasPrice())
.gasLimit(callParameter.getGasLimit()) .gasLimit(blockHeader.getGasLimit())
.to(callParameter.getTo()) .to(callParameter.getTo())
.sender(callParameter.getFrom()) .sender(callParameter.getFrom())
.value(callParameter.getValue()) .value(callParameter.getValue())
@ -155,7 +156,7 @@ public class TransactionSimulatorTest {
.type(TransactionType.FRONTIER) .type(TransactionType.FRONTIER)
.nonce(1L) .nonce(1L)
.gasPrice(Wei.ZERO) .gasPrice(Wei.ZERO)
.gasLimit(callParameter.getGasLimit()) .gasLimit(blockHeader.getGasLimit())
.to(callParameter.getTo()) .to(callParameter.getTo())
.sender(callParameter.getFrom()) .sender(callParameter.getFrom())
.value(callParameter.getValue()) .value(callParameter.getValue())
@ -175,7 +176,8 @@ public class TransactionSimulatorTest {
@Test @Test
public void shouldSetFeePerGasToZeroWhenExceedingBalanceAllowed() { public void shouldSetFeePerGasToZeroWhenExceedingBalanceAllowed() {
final CallParameter callParameter = eip1559TransactionCallParameter(Wei.ONE, Wei.ONE); final CallParameter callParameter =
eip1559TransactionCallParameter(Wei.ONE, Wei.ONE, TRANSFER_GAS_LIMIT);
final BlockHeader blockHeader = mockBlockHeader(Hash.ZERO, 1L, Wei.ONE); final BlockHeader blockHeader = mockBlockHeader(Hash.ZERO, 1L, Wei.ONE);
@ -187,7 +189,7 @@ public class TransactionSimulatorTest {
.type(TransactionType.EIP1559) .type(TransactionType.EIP1559)
.chainId(BigInteger.ONE) .chainId(BigInteger.ONE)
.nonce(1L) .nonce(1L)
.gasLimit(callParameter.getGasLimit()) .gasLimit(TRANSFER_GAS_LIMIT)
.maxFeePerGas(Wei.ZERO) .maxFeePerGas(Wei.ZERO)
.maxPriorityFeePerGas(Wei.ZERO) .maxPriorityFeePerGas(Wei.ZERO)
.to(callParameter.getTo()) .to(callParameter.getTo())
@ -223,7 +225,7 @@ public class TransactionSimulatorTest {
.type(TransactionType.FRONTIER) .type(TransactionType.FRONTIER)
.nonce(1L) .nonce(1L)
.gasPrice(callParameter.getGasPrice()) .gasPrice(callParameter.getGasPrice())
.gasLimit(callParameter.getGasLimit()) .gasLimit(blockHeader.getGasLimit())
.to(callParameter.getTo()) .to(callParameter.getTo())
.sender(callParameter.getFrom()) .sender(callParameter.getFrom())
.value(callParameter.getValue()) .value(callParameter.getValue())
@ -244,7 +246,8 @@ public class TransactionSimulatorTest {
@Test @Test
public void shouldNotSetFeePerGasToZeroWhenExceedingBalanceIsNotAllowed() { public void shouldNotSetFeePerGasToZeroWhenExceedingBalanceIsNotAllowed() {
final CallParameter callParameter = eip1559TransactionCallParameter(Wei.ONE, Wei.ONE); final CallParameter callParameter =
eip1559TransactionCallParameter(Wei.ONE, Wei.ONE, TRANSFER_GAS_LIMIT);
final BlockHeader blockHeader = mockBlockHeader(Hash.ZERO, 1L, Wei.ONE); final BlockHeader blockHeader = mockBlockHeader(Hash.ZERO, 1L, Wei.ONE);
@ -256,7 +259,7 @@ public class TransactionSimulatorTest {
.type(TransactionType.EIP1559) .type(TransactionType.EIP1559)
.chainId(BigInteger.ONE) .chainId(BigInteger.ONE)
.nonce(1L) .nonce(1L)
.gasLimit(callParameter.getGasLimit()) .gasLimit(TRANSFER_GAS_LIMIT)
.maxFeePerGas(callParameter.getMaxFeePerGas().orElseThrow()) .maxFeePerGas(callParameter.getMaxFeePerGas().orElseThrow())
.maxPriorityFeePerGas(callParameter.getMaxPriorityFeePerGas().orElseThrow()) .maxPriorityFeePerGas(callParameter.getMaxPriorityFeePerGas().orElseThrow())
.to(callParameter.getTo()) .to(callParameter.getTo())
@ -349,7 +352,7 @@ public class TransactionSimulatorTest {
.type(TransactionType.FRONTIER) .type(TransactionType.FRONTIER)
.nonce(1L) .nonce(1L)
.gasPrice(callParameter.getGasPrice()) .gasPrice(callParameter.getGasPrice())
.gasLimit(callParameter.getGasLimit()) .gasLimit(blockHeader.getGasLimit())
.to(callParameter.getTo()) .to(callParameter.getTo())
.sender(callParameter.getFrom()) .sender(callParameter.getFrom())
.value(callParameter.getValue()) .value(callParameter.getValue())
@ -390,7 +393,7 @@ public class TransactionSimulatorTest {
.type(TransactionType.FRONTIER) .type(TransactionType.FRONTIER)
.nonce(1L) .nonce(1L)
.gasPrice(callParameter.getGasPrice()) .gasPrice(callParameter.getGasPrice())
.gasLimit(callParameter.getGasLimit()) .gasLimit(blockHeader.getGasLimit())
.to(callParameter.getTo()) .to(callParameter.getTo())
.sender(callParameter.getFrom()) .sender(callParameter.getFrom())
.value(callParameter.getValue()) .value(callParameter.getValue())
@ -479,7 +482,7 @@ public class TransactionSimulatorTest {
.type(TransactionType.FRONTIER) .type(TransactionType.FRONTIER)
.nonce(1L) .nonce(1L)
.gasPrice(callParameter.getGasPrice()) .gasPrice(callParameter.getGasPrice())
.gasLimit(callParameter.getGasLimit()) .gasLimit(blockHeader.getGasLimit())
.to(callParameter.getTo()) .to(callParameter.getTo())
.sender(callParameter.getFrom()) .sender(callParameter.getFrom())
.value(callParameter.getValue()) .value(callParameter.getValue())
@ -509,7 +512,7 @@ public class TransactionSimulatorTest {
.type(TransactionType.EIP1559) .type(TransactionType.EIP1559)
.chainId(BigInteger.ONE) .chainId(BigInteger.ONE)
.nonce(1L) .nonce(1L)
.gasLimit(callParameter.getGasLimit()) .gasLimit(blockHeader.getGasLimit())
.maxFeePerGas(callParameter.getMaxFeePerGas().orElseThrow()) .maxFeePerGas(callParameter.getMaxFeePerGas().orElseThrow())
.maxPriorityFeePerGas(callParameter.getMaxPriorityFeePerGas().orElseThrow()) .maxPriorityFeePerGas(callParameter.getMaxPriorityFeePerGas().orElseThrow())
.to(callParameter.getTo()) .to(callParameter.getTo())
@ -530,7 +533,7 @@ public class TransactionSimulatorTest {
@Test @Test
public void shouldCapGasLimitWhenOriginalTransactionExceedsGasCap() { public void shouldCapGasLimitWhenOriginalTransactionExceedsGasCap() {
final CallParameter callParameter = final CallParameter callParameter =
eip1559TransactionCallParameter(Wei.ZERO, Wei.ZERO, GASCAP + 1); eip1559TransactionCallParameter(Wei.ZERO, Wei.ZERO, GAS_CAP + 1);
final BlockHeader blockHeader = mockBlockHeader(Hash.ZERO, 1L, Wei.ONE); final BlockHeader blockHeader = mockBlockHeader(Hash.ZERO, 1L, Wei.ONE);
@ -542,7 +545,7 @@ public class TransactionSimulatorTest {
.type(TransactionType.EIP1559) .type(TransactionType.EIP1559)
.chainId(BigInteger.ONE) .chainId(BigInteger.ONE)
.nonce(1L) .nonce(1L)
.gasLimit(GASCAP) .gasLimit(GAS_CAP)
.maxFeePerGas(callParameter.getMaxFeePerGas().orElseThrow()) .maxFeePerGas(callParameter.getMaxFeePerGas().orElseThrow())
.maxPriorityFeePerGas(callParameter.getMaxPriorityFeePerGas().orElseThrow()) .maxPriorityFeePerGas(callParameter.getMaxPriorityFeePerGas().orElseThrow())
.to(callParameter.getTo()) .to(callParameter.getTo())
@ -566,11 +569,48 @@ public class TransactionSimulatorTest {
} }
@Test @Test
public void shouldUseRpcGasCapWhenCapIsHigherThanGasLimit() { public void shouldUseProvidedGasLimitWhenBelowRpcCapGas() {
// generate a transaction with a gas limit that is lower than the gas cap,
// expect the gas cap to override parameter gas limit
final CallParameter callParameter = final CallParameter callParameter =
eip1559TransactionCallParameter(Wei.ZERO, Wei.ZERO, GASCAP - 1); eip1559TransactionCallParameter(Wei.ZERO, Wei.ZERO, GAS_CAP / 2);
final BlockHeader blockHeader = mockBlockHeader(Hash.ZERO, 1L, Wei.ONE);
mockBlockchainForBlockHeader(blockHeader);
mockWorldStateForAccount(blockHeader, callParameter.getFrom(), 1L);
final Transaction expectedTransaction =
Transaction.builder()
.type(TransactionType.EIP1559)
.chainId(BigInteger.ONE)
.nonce(1L)
.gasLimit(GAS_CAP / 2)
.maxFeePerGas(callParameter.getMaxFeePerGas().orElseThrow())
.maxPriorityFeePerGas(callParameter.getMaxPriorityFeePerGas().orElseThrow())
.to(callParameter.getTo())
.sender(callParameter.getFrom())
.value(callParameter.getValue())
.payload(callParameter.getPayload())
.signature(FAKE_SIGNATURE)
.build();
mockProtocolSpecForProcessWithWorldUpdater();
// call process with original transaction
cappedTransactionSimulator.process(
callParameter,
TransactionValidationParams.transactionSimulator(),
OperationTracer.NO_TRACING,
1L);
// expect overwritten transaction to be processed
verifyTransactionWasProcessed(expectedTransaction);
}
@Test
public void shouldUseRpcGasCapWhenGasLimitNoPresent() {
// generate call parameters that do not specify a gas limit,
// expect the rpc gas cap to be used for simulation
final CallParameter callParameter = eip1559TransactionCallParameter(Wei.ZERO, Wei.ZERO, -1);
final BlockHeader blockHeader = mockBlockHeader(Hash.ZERO, 1L, Wei.ONE); final BlockHeader blockHeader = mockBlockHeader(Hash.ZERO, 1L, Wei.ONE);
@ -591,7 +631,7 @@ public class TransactionSimulatorTest {
.value(callParameter.getValue()) .value(callParameter.getValue())
.payload(callParameter.getPayload()) .payload(callParameter.getPayload())
.signature(FAKE_SIGNATURE) .signature(FAKE_SIGNATURE)
.gasLimit(GASCAP) .gasLimit(GAS_CAP)
.build(); .build();
// call process with original transaction // call process with original transaction
@ -781,7 +821,7 @@ public class TransactionSimulatorTest {
return new CallParameter( return new CallParameter(
Address.fromHexString("0x0"), Address.fromHexString("0x0"),
Address.fromHexString("0x0"), Address.fromHexString("0x0"),
0, -1,
gasPrice, gasPrice,
Wei.of(0), Wei.of(0),
Bytes.EMPTY); Bytes.EMPTY);
@ -793,7 +833,7 @@ public class TransactionSimulatorTest {
private CallParameter eip1559TransactionCallParameter( private CallParameter eip1559TransactionCallParameter(
final Wei maxFeePerGas, final Wei maxPriorityFeePerGas) { final Wei maxFeePerGas, final Wei maxPriorityFeePerGas) {
return eip1559TransactionCallParameter(maxFeePerGas, maxPriorityFeePerGas, 0L); return eip1559TransactionCallParameter(maxFeePerGas, maxPriorityFeePerGas, -1);
} }
private CallParameter eip1559TransactionCallParameter( private CallParameter eip1559TransactionCallParameter(

@ -17,6 +17,8 @@ package org.hyperledger.besu.ethereum.eth.manager;
import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.eth.SnapProtocol; import org.hyperledger.besu.ethereum.eth.SnapProtocol;
import org.hyperledger.besu.ethereum.eth.manager.EthPeer.DisconnectCallback; import org.hyperledger.besu.ethereum.eth.manager.EthPeer.DisconnectCallback;
import org.hyperledger.besu.ethereum.eth.manager.exceptions.NoAvailablePeersException;
import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerSelector;
import org.hyperledger.besu.ethereum.eth.peervalidation.PeerValidator; import org.hyperledger.besu.ethereum.eth.peervalidation.PeerValidator;
import org.hyperledger.besu.ethereum.eth.sync.ChainHeadTracker; import org.hyperledger.besu.ethereum.eth.sync.ChainHeadTracker;
import org.hyperledger.besu.ethereum.eth.sync.SnapServerChecker; import org.hyperledger.besu.ethereum.eth.sync.SnapServerChecker;
@ -26,6 +28,7 @@ import org.hyperledger.besu.ethereum.forkid.ForkId;
import org.hyperledger.besu.ethereum.forkid.ForkIdManager; import org.hyperledger.besu.ethereum.forkid.ForkIdManager;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec; import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
import org.hyperledger.besu.ethereum.p2p.peers.Peer; import org.hyperledger.besu.ethereum.p2p.peers.Peer;
import org.hyperledger.besu.ethereum.p2p.peers.PeerId;
import org.hyperledger.besu.ethereum.p2p.rlpx.RlpxAgent; import org.hyperledger.besu.ethereum.p2p.rlpx.RlpxAgent;
import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnection; import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnection;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.messages.DisconnectMessage; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.messages.DisconnectMessage;
@ -61,7 +64,7 @@ import org.apache.tuweni.bytes.Bytes;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
public class EthPeers { public class EthPeers implements PeerSelector {
private static final Logger LOG = LoggerFactory.getLogger(EthPeers.class); private static final Logger LOG = LoggerFactory.getLogger(EthPeers.class);
public static final Comparator<EthPeer> TOTAL_DIFFICULTY = public static final Comparator<EthPeer> TOTAL_DIFFICULTY =
Comparator.comparing((final EthPeer p) -> p.chainState().getEstimatedTotalDifficulty()); Comparator.comparing((final EthPeer p) -> p.chainState().getEstimatedTotalDifficulty());
@ -465,6 +468,18 @@ public class EthPeers {
this.trailingPeerRequirementsSupplier = tprSupplier; this.trailingPeerRequirementsSupplier = tprSupplier;
} }
// Part of the PeerSelector interface, to be split apart later
@Override
public EthPeer getPeer(final Predicate<EthPeer> filter) {
return streamBestPeers().filter(filter).findFirst().orElseThrow(NoAvailablePeersException::new);
}
// Part of the PeerSelector interface, to be split apart later
@Override
public Optional<EthPeer> getPeerByPeerId(final PeerId peerId) {
return Optional.ofNullable(activeConnections.get(peerId.getId()));
}
@FunctionalInterface @FunctionalInterface
public interface ConnectCallback { public interface ConnectCallback {
void onPeerConnected(EthPeer newPeer); void onPeerConnected(EthPeer newPeer);

@ -1,102 +0,0 @@
/*
* Copyright contributors to Hyperledger Besu.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.eth.manager.peertask;
import org.hyperledger.besu.ethereum.eth.manager.EthPeer;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
import org.hyperledger.besu.ethereum.p2p.peers.PeerId;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.SubProtocol;
import java.util.Collection;
import java.util.Comparator;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Predicate;
import java.util.function.Supplier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This is a simple PeerSelector implementation that can be used the default implementation in most
* situations
*/
public class DefaultPeerSelector implements PeerSelector {
private static final Logger LOG = LoggerFactory.getLogger(DefaultPeerSelector.class);
private final Supplier<ProtocolSpec> protocolSpecSupplier;
private final Map<PeerId, EthPeer> ethPeersByPeerId = new ConcurrentHashMap<>();
public DefaultPeerSelector(final Supplier<ProtocolSpec> protocolSpecSupplier) {
this.protocolSpecSupplier = protocolSpecSupplier;
}
/**
* Gets the highest reputation peer matching the supplied filter
*
* @param filter a filter to match prospective peers with
* @return the highest reputation peer matching the supplies filter
* @throws NoAvailablePeerException If there are no suitable peers
*/
private EthPeer getPeer(final Predicate<EthPeer> filter) throws NoAvailablePeerException {
LOG.trace("Finding peer from pool of {} peers", ethPeersByPeerId.size());
return ethPeersByPeerId.values().stream()
.filter(filter)
.max(Comparator.naturalOrder())
.orElseThrow(NoAvailablePeerException::new);
}
@Override
public EthPeer getPeer(
final Collection<EthPeer> usedEthPeers,
final long requiredPeerHeight,
final SubProtocol requiredSubProtocol)
throws NoAvailablePeerException {
return getPeer(
(candidatePeer) ->
isPeerUnused(candidatePeer, usedEthPeers)
&& (protocolSpecSupplier.get().isPoS()
|| isPeerHeightHighEnough(candidatePeer, requiredPeerHeight))
&& isPeerProtocolSuitable(candidatePeer, requiredSubProtocol));
}
@Override
public Optional<EthPeer> getPeerByPeerId(final PeerId peerId) {
return Optional.ofNullable(ethPeersByPeerId.get(peerId));
}
@Override
public void addPeer(final EthPeer ethPeer) {
ethPeersByPeerId.put(ethPeer.getConnection().getPeer(), ethPeer);
}
@Override
public void removePeer(final PeerId peerId) {
ethPeersByPeerId.remove(peerId);
}
private boolean isPeerUnused(final EthPeer ethPeer, final Collection<EthPeer> usedEthPeers) {
return !usedEthPeers.contains(ethPeer);
}
private boolean isPeerHeightHighEnough(final EthPeer ethPeer, final long requiredHeight) {
return ethPeer.chainState().getEstimatedHeight() >= requiredHeight;
}
private boolean isPeerProtocolSuitable(final EthPeer ethPeer, final SubProtocol protocol) {
return ethPeer.getProtocolName().equals(protocol.getName());
}
}

@ -16,28 +16,20 @@ package org.hyperledger.besu.ethereum.eth.manager.peertask;
import org.hyperledger.besu.ethereum.eth.manager.EthPeer; import org.hyperledger.besu.ethereum.eth.manager.EthPeer;
import org.hyperledger.besu.ethereum.p2p.peers.PeerId; import org.hyperledger.besu.ethereum.p2p.peers.PeerId;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.SubProtocol;
import java.util.Collection;
import java.util.Optional; import java.util.Optional;
import java.util.function.Predicate;
/** Selects the EthPeers for the PeerTaskExecutor */ /** Selects the EthPeers for the PeerTaskExecutor */
public interface PeerSelector { public interface PeerSelector {
/** /**
* Gets a peer with the requiredPeerHeight (if not PoS), and with the requiredSubProtocol, and * Gets a peer matching the supplied filter
* which is not in the supplied collection of usedEthPeers
* *
* @param usedEthPeers a collection of EthPeers to be excluded from selection because they have * @param filter a Predicate\<EthPeer\> matching desirable peers
* already been used
* @param requiredPeerHeight the minimum peer height required of the selected peer
* @param requiredSubProtocol the SubProtocol required of the peer
* @return a peer matching the supplied conditions * @return a peer matching the supplied conditions
* @throws NoAvailablePeerException If there are no suitable peers
*/ */
EthPeer getPeer( EthPeer getPeer(final Predicate<EthPeer> filter);
Collection<EthPeer> usedEthPeers, long requiredPeerHeight, SubProtocol requiredSubProtocol)
throws NoAvailablePeerException;
/** /**
* Attempts to get the EthPeer identified by peerId * Attempts to get the EthPeer identified by peerId
@ -47,18 +39,4 @@ public interface PeerSelector {
* PeerSelector, or empty otherwise * PeerSelector, or empty otherwise
*/ */
Optional<EthPeer> getPeerByPeerId(PeerId peerId); Optional<EthPeer> getPeerByPeerId(PeerId peerId);
/**
* Add the supplied EthPeer to the PeerSelector
*
* @param ethPeer the EthPeer to be added to the PeerSelector
*/
void addPeer(EthPeer ethPeer);
/**
* Remove the EthPeer identified by peerId from the PeerSelector
*
* @param peerId the PeerId of the EthPeer to be removed from the PeerSelector
*/
void removePeer(PeerId peerId);
} }

@ -14,10 +14,12 @@
*/ */
package org.hyperledger.besu.ethereum.eth.manager.peertask; package org.hyperledger.besu.ethereum.eth.manager.peertask;
import org.hyperledger.besu.ethereum.eth.manager.EthPeer;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.SubProtocol; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.SubProtocol;
import java.util.Collection; import java.util.Collection;
import java.util.function.Predicate;
/** /**
* Represents a task to be executed on an EthPeer by the PeerTaskExecutor * Represents a task to be executed on an EthPeer by the PeerTaskExecutor
@ -32,13 +34,6 @@ public interface PeerTask<T> {
*/ */
SubProtocol getSubProtocol(); SubProtocol getSubProtocol();
/**
* Gets the minimum required block number for a peer to have to successfully execute this task
*
* @return the minimum required block number for a peer to have to successfully execute this task
*/
long getRequiredBlockNumber();
/** /**
* Gets the request data to send to the EthPeer * Gets the request data to send to the EthPeer
* *
@ -61,4 +56,11 @@ public interface PeerTask<T> {
* @return the Collection of behaviors this task is expected to exhibit in the PeetTaskExecutor * @return the Collection of behaviors this task is expected to exhibit in the PeetTaskExecutor
*/ */
Collection<PeerTaskRetryBehavior> getPeerTaskBehaviors(); Collection<PeerTaskRetryBehavior> getPeerTaskBehaviors();
/**
* Gets a Predicate that checks if an EthPeer is suitable for this PeerTask
*
* @return a Predicate that checks if an EthPeer is suitable for this PeerTask
*/
Predicate<EthPeer> getPeerRequirementFilter();
} }

@ -16,6 +16,7 @@ package org.hyperledger.besu.ethereum.eth.manager.peertask;
import org.hyperledger.besu.ethereum.eth.manager.EthPeer; import org.hyperledger.besu.ethereum.eth.manager.EthPeer;
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler; import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
import org.hyperledger.besu.ethereum.eth.manager.exceptions.NoAvailablePeersException;
import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnection; import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnection;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData;
import org.hyperledger.besu.metrics.BesuMetricCategory; import org.hyperledger.besu.metrics.BesuMetricCategory;
@ -70,10 +71,12 @@ public class PeerTaskExecutor {
try { try {
peer = peer =
peerSelector.getPeer( peerSelector.getPeer(
usedEthPeers, peerTask.getRequiredBlockNumber(), peerTask.getSubProtocol()); (candidatePeer) ->
peerTask.getPeerRequirementFilter().test(candidatePeer)
&& !usedEthPeers.contains(candidatePeer));
usedEthPeers.add(peer); usedEthPeers.add(peer);
executorResult = executeAgainstPeer(peerTask, peer); executorResult = executeAgainstPeer(peerTask, peer);
} catch (NoAvailablePeerException e) { } catch (NoAvailablePeersException e) {
executorResult = executorResult =
new PeerTaskExecutorResult<>( new PeerTaskExecutorResult<>(
Optional.empty(), PeerTaskExecutorResponseCode.NO_PEER_AVAILABLE); Optional.empty(), PeerTaskExecutorResponseCode.NO_PEER_AVAILABLE);
@ -89,11 +92,6 @@ public class PeerTaskExecutor {
() -> CompletableFuture.completedFuture(execute(peerTask))); () -> CompletableFuture.completedFuture(execute(peerTask)));
} }
public <T> Collection<CompletableFuture<PeerTaskExecutorResult<T>>> executeBatchAsync(
final Collection<PeerTask<T>> peerTasks) {
return peerTasks.stream().map(this::executeAsync).toList();
}
public <T> PeerTaskExecutorResult<T> executeAgainstPeer( public <T> PeerTaskExecutorResult<T> executeAgainstPeer(
final PeerTask<T> peerTask, final EthPeer peer) { final PeerTask<T> peerTask, final EthPeer peer) {
MessageData requestMessageData = peerTask.getRequestMessage(); MessageData requestMessageData = peerTask.getRequestMessage();
@ -148,7 +146,8 @@ public class PeerTaskExecutor {
public <T> CompletableFuture<PeerTaskExecutorResult<T>> executeAgainstPeerAsync( public <T> CompletableFuture<PeerTaskExecutorResult<T>> executeAgainstPeerAsync(
final PeerTask<T> peerTask, final EthPeer peer) { final PeerTask<T> peerTask, final EthPeer peer) {
return CompletableFuture.supplyAsync(() -> executeAgainstPeer(peerTask, peer)); return ethScheduler.scheduleSyncWorkerTask(
() -> CompletableFuture.completedFuture(executeAgainstPeer(peerTask, peer)));
} }
private boolean sleepBetweenRetries() { private boolean sleepBetweenRetries() {

@ -16,7 +16,6 @@ package org.hyperledger.besu.ethereum.eth.sync.snapsync.request;
import static org.hyperledger.besu.ethereum.eth.sync.snapsync.RequestType.STORAGE_RANGE; import static org.hyperledger.besu.ethereum.eth.sync.snapsync.RequestType.STORAGE_RANGE;
import static org.hyperledger.besu.ethereum.eth.sync.snapsync.StackTrie.FlatDatabaseUpdater.noop; import static org.hyperledger.besu.ethereum.eth.sync.snapsync.StackTrie.FlatDatabaseUpdater.noop;
import static org.hyperledger.besu.ethereum.trie.RangeManager.MAX_RANGE;
import static org.hyperledger.besu.ethereum.trie.RangeManager.MIN_RANGE; import static org.hyperledger.besu.ethereum.trie.RangeManager.MIN_RANGE;
import static org.hyperledger.besu.ethereum.trie.RangeManager.findNewBeginElementInRange; import static org.hyperledger.besu.ethereum.trie.RangeManager.findNewBeginElementInRange;
import static org.hyperledger.besu.ethereum.trie.RangeManager.getRangeCount; import static org.hyperledger.besu.ethereum.trie.RangeManager.getRangeCount;
@ -192,12 +191,13 @@ public class StorageRangeDataRequest extends SnapDataRequest {
getRootHash(), accountHash, storageRoot, key, value); getRootHash(), accountHash, storageRoot, key, value);
childRequests.add(storageRangeDataRequest); childRequests.add(storageRangeDataRequest);
}); });
if (startKeyHash.equals(MIN_RANGE) && endKeyHash.equals(MAX_RANGE)) {
// need to heal this account storage
downloadState.addAccountToHealingList(CompactEncoding.bytesToPath(accountHash));
}
}); });
if (startKeyHash.equals(MIN_RANGE) && !taskElement.proofs().isEmpty()) {
// need to heal this account storage
downloadState.addAccountToHealingList(CompactEncoding.bytesToPath(accountHash));
}
return childRequests.stream(); return childRequests.stream();
} }

@ -1,135 +0,0 @@
/*
* Copyright contributors to Hyperledger Besu.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.eth.manager.peertask;
import org.hyperledger.besu.ethereum.eth.EthProtocol;
import org.hyperledger.besu.ethereum.eth.SnapProtocol;
import org.hyperledger.besu.ethereum.eth.manager.ChainState;
import org.hyperledger.besu.ethereum.eth.manager.EthPeer;
import org.hyperledger.besu.ethereum.eth.manager.PeerReputation;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
import org.hyperledger.besu.ethereum.p2p.peers.Peer;
import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnection;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.SubProtocol;
import java.util.HashSet;
import java.util.Set;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
public class DefaultPeerSelectorTest {
public DefaultPeerSelector peerSelector;
@BeforeEach
public void beforeTest() {
ProtocolSpec protocolSpec = Mockito.mock(ProtocolSpec.class);
Mockito.when(protocolSpec.isPoS()).thenReturn(false);
peerSelector = new DefaultPeerSelector(() -> protocolSpec);
}
@Test
public void testGetPeer() throws NoAvailablePeerException {
EthPeer expectedPeer = createTestPeer(10, EthProtocol.get(), 5);
peerSelector.addPeer(expectedPeer);
EthPeer excludedForLowChainHeightPeer = createTestPeer(5, EthProtocol.get(), 50);
peerSelector.addPeer(excludedForLowChainHeightPeer);
EthPeer excludedForWrongProtocolPeer = createTestPeer(10, SnapProtocol.get(), 50);
peerSelector.addPeer(excludedForWrongProtocolPeer);
EthPeer excludedForLowReputationPeer = createTestPeer(10, EthProtocol.get(), 1);
peerSelector.addPeer(excludedForLowReputationPeer);
EthPeer excludedForBeingAlreadyUsedPeer = createTestPeer(10, EthProtocol.get(), 50);
peerSelector.addPeer(excludedForBeingAlreadyUsedPeer);
Set<EthPeer> usedEthPeers = new HashSet<>();
usedEthPeers.add(excludedForBeingAlreadyUsedPeer);
EthPeer result = peerSelector.getPeer(usedEthPeers, 10, EthProtocol.get());
Assertions.assertSame(expectedPeer, result);
}
@Test
public void testGetPeerButNoPeerMatchesFilter() {
EthPeer expectedPeer = createTestPeer(10, EthProtocol.get(), 5);
peerSelector.addPeer(expectedPeer);
EthPeer excludedForLowChainHeightPeer = createTestPeer(5, EthProtocol.get(), 50);
peerSelector.addPeer(excludedForLowChainHeightPeer);
EthPeer excludedForWrongProtocolPeer = createTestPeer(10, SnapProtocol.get(), 50);
peerSelector.addPeer(excludedForWrongProtocolPeer);
EthPeer excludedForLowReputationPeer = createTestPeer(10, EthProtocol.get(), 1);
peerSelector.addPeer(excludedForLowReputationPeer);
EthPeer excludedForBeingAlreadyUsedPeer = createTestPeer(10, EthProtocol.get(), 50);
peerSelector.addPeer(excludedForBeingAlreadyUsedPeer);
Set<EthPeer> usedEthPeers = new HashSet<>();
usedEthPeers.add(excludedForBeingAlreadyUsedPeer);
Assertions.assertThrows(
NoAvailablePeerException.class,
() -> peerSelector.getPeer(usedEthPeers, 10, new MockSubProtocol()));
}
private EthPeer createTestPeer(
final long chainHeight, final SubProtocol protocol, final int reputation) {
EthPeer ethPeer = Mockito.mock(EthPeer.class);
PeerConnection peerConnection = Mockito.mock(PeerConnection.class);
Peer peer = Mockito.mock(Peer.class);
ChainState chainState = Mockito.mock(ChainState.class);
PeerReputation peerReputation = Mockito.mock(PeerReputation.class);
Mockito.when(ethPeer.getConnection()).thenReturn(peerConnection);
Mockito.when(peerConnection.getPeer()).thenReturn(peer);
Mockito.when(ethPeer.getProtocolName()).thenReturn(protocol.getName());
Mockito.when(ethPeer.chainState()).thenReturn(chainState);
Mockito.when(chainState.getEstimatedHeight()).thenReturn(chainHeight);
Mockito.when(ethPeer.getReputation()).thenReturn(peerReputation);
Mockito.when(peerReputation.getScore()).thenReturn(reputation);
Mockito.when(ethPeer.compareTo(Mockito.any(EthPeer.class)))
.thenAnswer(
(invocationOnMock) -> {
EthPeer otherPeer = invocationOnMock.getArgument(0, EthPeer.class);
return Integer.compare(reputation, otherPeer.getReputation().getScore());
});
return ethPeer;
}
private static class MockSubProtocol implements SubProtocol {
@Override
public String getName() {
return "Mock";
}
@Override
public int messageSpace(final int protocolVersion) {
throw new UnsupportedOperationException();
}
@Override
public boolean isValidMessageCode(final int protocolVersion, final int code) {
throw new UnsupportedOperationException();
}
@Override
public String messageName(final int protocolVersion, final int code) {
throw new UnsupportedOperationException();
}
}
}

@ -15,16 +15,17 @@
package org.hyperledger.besu.ethereum.eth.manager.peertask; package org.hyperledger.besu.ethereum.eth.manager.peertask;
import org.hyperledger.besu.ethereum.eth.manager.EthPeer; import org.hyperledger.besu.ethereum.eth.manager.EthPeer;
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnection; import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnection;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.SubProtocol; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.SubProtocol;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import java.util.function.Predicate;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assertions;
@ -37,6 +38,7 @@ import org.mockito.MockitoAnnotations;
public class PeerTaskExecutorTest { public class PeerTaskExecutorTest {
private @Mock PeerSelector peerSelector; private @Mock PeerSelector peerSelector;
private @Mock PeerTaskRequestSender requestSender; private @Mock PeerTaskRequestSender requestSender;
private @Mock EthScheduler ethScheduler;
private @Mock PeerTask<Object> peerTask; private @Mock PeerTask<Object> peerTask;
private @Mock SubProtocol subprotocol; private @Mock SubProtocol subprotocol;
private @Mock MessageData requestMessageData; private @Mock MessageData requestMessageData;
@ -50,7 +52,7 @@ public class PeerTaskExecutorTest {
public void beforeTest() { public void beforeTest() {
mockCloser = MockitoAnnotations.openMocks(this); mockCloser = MockitoAnnotations.openMocks(this);
peerTaskExecutor = peerTaskExecutor =
new PeerTaskExecutor(peerSelector, requestSender, null, new NoOpMetricsSystem()); new PeerTaskExecutor(peerSelector, requestSender, ethScheduler, new NoOpMetricsSystem());
} }
@AfterEach @AfterEach
@ -202,14 +204,10 @@ public class PeerTaskExecutorTest {
NoAvailablePeerException { NoAvailablePeerException {
Object responseObject = new Object(); Object responseObject = new Object();
Mockito.when( Mockito.when(peerSelector.getPeer(Mockito.any(Predicate.class))).thenReturn(ethPeer);
peerSelector.getPeer(
Mockito.any(Collection.class), Mockito.eq(10L), Mockito.eq(subprotocol)))
.thenReturn(ethPeer);
Mockito.when(peerTask.getRequestMessage()).thenReturn(requestMessageData); Mockito.when(peerTask.getRequestMessage()).thenReturn(requestMessageData);
Mockito.when(peerTask.getPeerTaskBehaviors()).thenReturn(Collections.emptyList()); Mockito.when(peerTask.getPeerTaskBehaviors()).thenReturn(Collections.emptyList());
Mockito.when(peerTask.getRequiredBlockNumber()).thenReturn(10L);
Mockito.when(peerTask.getSubProtocol()).thenReturn(subprotocol); Mockito.when(peerTask.getSubProtocol()).thenReturn(subprotocol);
Mockito.when(subprotocol.getName()).thenReturn("subprotocol"); Mockito.when(subprotocol.getName()).thenReturn("subprotocol");
Mockito.when(requestSender.sendRequest(subprotocol, requestMessageData, ethPeer)) Mockito.when(requestSender.sendRequest(subprotocol, requestMessageData, ethPeer))
@ -233,22 +231,18 @@ public class PeerTaskExecutorTest {
ExecutionException, ExecutionException,
InterruptedException, InterruptedException,
TimeoutException, TimeoutException,
InvalidPeerTaskResponseException, InvalidPeerTaskResponseException {
NoAvailablePeerException {
Object responseObject = new Object(); Object responseObject = new Object();
int requestMessageDataCode = 123; int requestMessageDataCode = 123;
EthPeer peer2 = Mockito.mock(EthPeer.class); EthPeer peer2 = Mockito.mock(EthPeer.class);
Mockito.when( Mockito.when(peerSelector.getPeer(Mockito.any(Predicate.class)))
peerSelector.getPeer(
Mockito.any(Collection.class), Mockito.eq(10L), Mockito.eq(subprotocol)))
.thenReturn(ethPeer) .thenReturn(ethPeer)
.thenReturn(peer2); .thenReturn(peer2);
Mockito.when(peerTask.getRequestMessage()).thenReturn(requestMessageData); Mockito.when(peerTask.getRequestMessage()).thenReturn(requestMessageData);
Mockito.when(peerTask.getPeerTaskBehaviors()) Mockito.when(peerTask.getPeerTaskBehaviors())
.thenReturn(List.of(PeerTaskRetryBehavior.RETRY_WITH_OTHER_PEERS)); .thenReturn(List.of(PeerTaskRetryBehavior.RETRY_WITH_OTHER_PEERS));
Mockito.when(peerTask.getRequiredBlockNumber()).thenReturn(10L);
Mockito.when(peerTask.getSubProtocol()).thenReturn(subprotocol); Mockito.when(peerTask.getSubProtocol()).thenReturn(subprotocol);
Mockito.when(requestSender.sendRequest(subprotocol, requestMessageData, ethPeer)) Mockito.when(requestSender.sendRequest(subprotocol, requestMessageData, ethPeer))
.thenThrow(new TimeoutException()); .thenThrow(new TimeoutException());

@ -64,9 +64,7 @@ public class AccountHealingTrackingTest {
DataStorageConfiguration.DEFAULT_BONSAI_CONFIG); DataStorageConfiguration.DEFAULT_BONSAI_CONFIG);
private WorldStateStorageCoordinator worldStateStorageCoordinator; private WorldStateStorageCoordinator worldStateStorageCoordinator;
private WorldStateProofProvider worldStateProofProvider; private WorldStateProofProvider worldStateProofProvider;
private MerkleTrie<Bytes, Bytes> accountStateTrie; private MerkleTrie<Bytes, Bytes> accountStateTrie;
@Mock SnapWorldDownloadState snapWorldDownloadState; @Mock SnapWorldDownloadState snapWorldDownloadState;
@ -82,9 +80,7 @@ public class AccountHealingTrackingTest {
} }
@Test @Test
void avoidMarkingAccountWhenStorageProofValid() { void shouldMarkAccountForHealingWhenStorageProofIsReceived() {
// generate valid proof
final Hash accountHash = Hash.hash(accounts.get(0)); final Hash accountHash = Hash.hash(accounts.get(0));
final StateTrieAccountValue stateTrieAccountValue = final StateTrieAccountValue stateTrieAccountValue =
StateTrieAccountValue.readFrom(RLP.input(accountStateTrie.get(accountHash).orElseThrow())); StateTrieAccountValue.readFrom(RLP.input(accountStateTrie.get(accountHash).orElseThrow()));
@ -108,7 +104,7 @@ public class AccountHealingTrackingTest {
root -> root ->
RangeStorageEntriesCollector.collectEntries( RangeStorageEntriesCollector.collectEntries(
collector, visitor, root, Hash.ZERO)); collector, visitor, root, Hash.ZERO));
// generate the proof
final List<Bytes> proofs = final List<Bytes> proofs =
worldStateProofProvider.getStorageProofRelatedNodes( worldStateProofProvider.getStorageProofRelatedNodes(
Hash.wrap(storageTrie.getRootHash()), accountHash, Hash.ZERO); Hash.wrap(storageTrie.getRootHash()), accountHash, Hash.ZERO);
@ -127,11 +123,53 @@ public class AccountHealingTrackingTest {
snapWorldDownloadState, worldStateProofProvider, slots, new ArrayDeque<>(proofs)); snapWorldDownloadState, worldStateProofProvider, slots, new ArrayDeque<>(proofs));
storageRangeDataRequest.getChildRequests( storageRangeDataRequest.getChildRequests(
snapWorldDownloadState, worldStateStorageCoordinator, null); snapWorldDownloadState, worldStateStorageCoordinator, null);
verify(snapWorldDownloadState).addAccountToHealingList(any(Bytes.class));
}
@Test
void shouldNotMarkAccountForHealingWhenAllStorageIsReceivedWithoutProof() {
final Hash accountHash = Hash.hash(accounts.get(0));
final StateTrieAccountValue stateTrieAccountValue =
StateTrieAccountValue.readFrom(RLP.input(accountStateTrie.get(accountHash).orElseThrow()));
final StoredMerklePatriciaTrie<Bytes, Bytes> storageTrie =
new StoredMerklePatriciaTrie<>(
new StoredNodeFactory<>(
(location, hash) ->
worldStateKeyValueStorage.getAccountStorageTrieNode(
accountHash, location, hash),
Function.identity(),
Function.identity()),
stateTrieAccountValue.getStorageRoot());
final RangeStorageEntriesCollector collector =
RangeStorageEntriesCollector.createCollector(Hash.ZERO, MAX_RANGE, 10, Integer.MAX_VALUE);
final TrieIterator<Bytes> visitor = RangeStorageEntriesCollector.createVisitor(collector);
final TreeMap<Bytes32, Bytes> slots =
(TreeMap<Bytes32, Bytes>)
storageTrie.entriesFrom(
root ->
RangeStorageEntriesCollector.collectEntries(
collector, visitor, root, Hash.ZERO));
final StorageRangeDataRequest storageRangeDataRequest =
SnapDataRequest.createStorageRangeDataRequest(
Hash.wrap(accountStateTrie.getRootHash()),
accountHash,
storageTrie.getRootHash(),
Hash.ZERO,
MAX_RANGE);
storageRangeDataRequest.addResponse(
snapWorldDownloadState, worldStateProofProvider, slots, new ArrayDeque<>());
storageRangeDataRequest.getChildRequests(
snapWorldDownloadState, worldStateStorageCoordinator, null);
verify(snapWorldDownloadState, never()).addAccountToHealingList(any(Bytes.class)); verify(snapWorldDownloadState, never()).addAccountToHealingList(any(Bytes.class));
} }
@Test @Test
void markAccountOnInvalidStorageProof() { void shouldMarkAccountForHealingOnInvalidStorageProof() {
final Hash accountHash = Hash.hash(accounts.get(0)); final Hash accountHash = Hash.hash(accounts.get(0));
final StateTrieAccountValue stateTrieAccountValue = final StateTrieAccountValue stateTrieAccountValue =
StateTrieAccountValue.readFrom(RLP.input(accountStateTrie.get(accountHash).orElseThrow())); StateTrieAccountValue.readFrom(RLP.input(accountStateTrie.get(accountHash).orElseThrow()));
@ -157,8 +195,7 @@ public class AccountHealingTrackingTest {
} }
@Test @Test
void markAccountOnPartialStorageRange() { void shouldMarkAccountForHealingOnInvalidStorageWithoutProof() {
// generate valid proof
final Hash accountHash = Hash.hash(accounts.get(0)); final Hash accountHash = Hash.hash(accounts.get(0));
final StateTrieAccountValue stateTrieAccountValue = final StateTrieAccountValue stateTrieAccountValue =
StateTrieAccountValue.readFrom(RLP.input(accountStateTrie.get(accountHash).orElseThrow())); StateTrieAccountValue.readFrom(RLP.input(accountStateTrie.get(accountHash).orElseThrow()));
@ -174,11 +211,46 @@ public class AccountHealingTrackingTest {
stateTrieAccountValue.getStorageRoot()); stateTrieAccountValue.getStorageRoot());
final RangeStorageEntriesCollector collector = final RangeStorageEntriesCollector collector =
RangeStorageEntriesCollector.createCollector( RangeStorageEntriesCollector.createCollector(Hash.ZERO, MAX_RANGE, 1, Integer.MAX_VALUE);
final TrieIterator<Bytes> visitor = RangeStorageEntriesCollector.createVisitor(collector);
final TreeMap<Bytes32, Bytes> slots =
(TreeMap<Bytes32, Bytes>)
storageTrie.entriesFrom(
root ->
RangeStorageEntriesCollector.collectEntries(
collector, visitor, root, Hash.ZERO));
final StorageRangeDataRequest storageRangeDataRequest =
SnapDataRequest.createStorageRangeDataRequest(
Hash.wrap(accountStateTrie.getRootHash()),
accountHash,
storageTrie.getRootHash(),
Hash.ZERO, Hash.ZERO,
MAX_RANGE, MAX_RANGE);
1, storageRangeDataRequest.addResponse(
Integer.MAX_VALUE); // limit to 1 in order to have a partial range snapWorldDownloadState, worldStateProofProvider, slots, new ArrayDeque<>());
verify(snapWorldDownloadState).addAccountToHealingList(any(Bytes.class));
}
@Test
void shouldMarkAccountForHealingOnPartialStorageRange() {
final Hash accountHash = Hash.hash(accounts.get(0));
final StateTrieAccountValue stateTrieAccountValue =
StateTrieAccountValue.readFrom(RLP.input(accountStateTrie.get(accountHash).orElseThrow()));
final StoredMerklePatriciaTrie<Bytes, Bytes> storageTrie =
new StoredMerklePatriciaTrie<>(
new StoredNodeFactory<>(
(location, hash) ->
worldStateKeyValueStorage.getAccountStorageTrieNode(
accountHash, location, hash),
Function.identity(),
Function.identity()),
stateTrieAccountValue.getStorageRoot());
final RangeStorageEntriesCollector collector =
RangeStorageEntriesCollector.createCollector(Hash.ZERO, MAX_RANGE, 1, Integer.MAX_VALUE);
final TrieIterator<Bytes> visitor = RangeStorageEntriesCollector.createVisitor(collector); final TrieIterator<Bytes> visitor = RangeStorageEntriesCollector.createVisitor(collector);
final TreeMap<Bytes32, Bytes> slots = final TreeMap<Bytes32, Bytes> slots =
(TreeMap<Bytes32, Bytes>) (TreeMap<Bytes32, Bytes>)
@ -186,7 +258,7 @@ public class AccountHealingTrackingTest {
root -> root ->
RangeStorageEntriesCollector.collectEntries( RangeStorageEntriesCollector.collectEntries(
collector, visitor, root, Hash.ZERO)); collector, visitor, root, Hash.ZERO));
// generate the proof
final List<Bytes> proofs = final List<Bytes> proofs =
worldStateProofProvider.getStorageProofRelatedNodes( worldStateProofProvider.getStorageProofRelatedNodes(
Hash.wrap(storageTrie.getRootHash()), accountHash, Hash.ZERO); Hash.wrap(storageTrie.getRootHash()), accountHash, Hash.ZERO);
@ -205,14 +277,14 @@ public class AccountHealingTrackingTest {
snapWorldDownloadState, worldStateProofProvider, slots, new ArrayDeque<>(proofs)); snapWorldDownloadState, worldStateProofProvider, slots, new ArrayDeque<>(proofs));
verify(snapWorldDownloadState, never()).addAccountToHealingList(any(Bytes.class)); verify(snapWorldDownloadState, never()).addAccountToHealingList(any(Bytes.class));
// should mark during the getchild request
storageRangeDataRequest.getChildRequests( storageRangeDataRequest.getChildRequests(
snapWorldDownloadState, worldStateStorageCoordinator, null); snapWorldDownloadState, worldStateStorageCoordinator, null);
verify(snapWorldDownloadState).addAccountToHealingList(any(Bytes.class)); verify(snapWorldDownloadState).addAccountToHealingList(any(Bytes.class));
} }
@Test @Test
void avoidMarkingAccountOnValidStorageTrieNodeDetection() { void shouldNotMarkAccountForHealingOnValidStorageTrieNodeDetection() {
final Hash accountHash = Hash.hash(accounts.get(0)); final Hash accountHash = Hash.hash(accounts.get(0));
final StateTrieAccountValue stateTrieAccountValue = final StateTrieAccountValue stateTrieAccountValue =
StateTrieAccountValue.readFrom(RLP.input(accountStateTrie.get(accountHash).orElseThrow())); StateTrieAccountValue.readFrom(RLP.input(accountStateTrie.get(accountHash).orElseThrow()));
@ -223,6 +295,7 @@ public class AccountHealingTrackingTest {
Hash.wrap(accountStateTrie.getRootHash()), Hash.wrap(accountStateTrie.getRootHash()),
Bytes.EMPTY); Bytes.EMPTY);
storageTrieNodeHealingRequest.getExistingData(worldStateStorageCoordinator); storageTrieNodeHealingRequest.getExistingData(worldStateStorageCoordinator);
verify(snapWorldDownloadState, never()).addAccountToHealingList(any(Bytes.class)); verify(snapWorldDownloadState, never()).addAccountToHealingList(any(Bytes.class));
} }
} }

@ -0,0 +1,39 @@
/*
* Copyright contributors to Hyperledger Besu.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.p2p.discovery;
import org.hyperledger.besu.util.number.Percentage;
import java.util.Collection;
import java.util.List;
import org.apache.commons.net.util.SubnetUtils;
import org.apache.tuweni.bytes.Bytes;
public record P2PDiscoveryConfiguration(
Boolean p2pEnabled,
Boolean peerDiscoveryEnabled,
String p2pHost,
String p2pInterface,
Integer p2pPort,
Integer maxPeers,
Boolean isLimitRemoteWireConnectionsEnabled,
Percentage maxRemoteConnectionsPercentage,
Boolean randomPeerPriority,
Collection<Bytes> bannedNodeIds,
List<SubnetUtils.SubnetInfo> allowedSubnets,
Boolean poaDiscoveryRetryBootnodes,
List<String> bootNodes,
String discoveryDnsUrl) {}

@ -14,8 +14,6 @@
*/ */
package org.hyperledger.besu.ethereum.p2p.discovery.dns; package org.hyperledger.besu.ethereum.p2p.discovery.dns;
import static org.assertj.core.api.Assertions.assertThat;
import java.security.Security; import java.security.Security;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
@ -26,7 +24,6 @@ import io.vertx.junit5.Checkpoint;
import io.vertx.junit5.VertxExtension; import io.vertx.junit5.VertxExtension;
import io.vertx.junit5.VertxTestContext; import io.vertx.junit5.VertxTestContext;
import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.DisplayName;
@ -136,10 +133,4 @@ class DNSDaemonTest {
.setWorkerPoolSize(1); .setWorkerPoolSize(1);
vertx.deployVerticle(dnsDaemon, options); vertx.deployVerticle(dnsDaemon, options);
} }
@AfterEach
@DisplayName("Check that the vertx worker verticle is still there")
void lastChecks(final Vertx vertx) {
assertThat(vertx.deploymentIDs()).isNotEmpty().hasSize(2);
}
} }

@ -16,6 +16,7 @@ package org.hyperledger.besu.evm.operation;
import static org.hyperledger.besu.evm.internal.Words.clampedToInt; import static org.hyperledger.besu.evm.internal.Words.clampedToInt;
import org.hyperledger.besu.evm.Code;
import org.hyperledger.besu.evm.EVM; import org.hyperledger.besu.evm.EVM;
import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.GasCalculator; import org.hyperledger.besu.evm.gascalculator.GasCalculator;
@ -37,6 +38,11 @@ public class ReturnDataLoadOperation extends AbstractOperation {
@Override @Override
public OperationResult execute(final MessageFrame frame, final EVM evm) { public OperationResult execute(final MessageFrame frame, final EVM evm) {
Code code = frame.getCode();
if (code.getEofVersion() == 0) {
return InvalidOperation.INVALID_RESULT;
}
final int offset = clampedToInt(frame.popStackItem()); final int offset = clampedToInt(frame.popStackItem());
Bytes returnData = frame.getReturnData(); Bytes returnData = frame.getReturnData();
int retunDataSize = returnData.size(); int retunDataSize = returnData.size();

@ -71,7 +71,7 @@ Calculated : ${currentHash}
tasks.register('checkAPIChanges', FileStateChecker) { tasks.register('checkAPIChanges', FileStateChecker) {
description = "Checks that the API for the Plugin-API project does not change without deliberate thought" description = "Checks that the API for the Plugin-API project does not change without deliberate thought"
files = sourceSets.main.allJava.files files = sourceSets.main.allJava.files
knownHash = '5H+3gUzCwZtLByfnk11kf+kAPwykQ+WR+n3xWgyfsyY=' knownHash = '4jVaj9yW88nHbX0KmTR3dPQRvj9x8Pvh5E9Ry7KRT6w='
} }
check.dependsOn('checkAPIChanges') check.dependsOn('checkAPIChanges')

@ -22,6 +22,7 @@ import org.hyperledger.besu.plugin.data.BlockContext;
import org.hyperledger.besu.plugin.data.BlockHeader; import org.hyperledger.besu.plugin.data.BlockHeader;
import org.hyperledger.besu.plugin.data.TransactionReceipt; import org.hyperledger.besu.plugin.data.TransactionReceipt;
import java.math.BigInteger;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
@ -106,5 +107,11 @@ public interface BlockchainService extends BesuService {
* @throws UnsupportedOperationException if the network is a PoS network * @throws UnsupportedOperationException if the network is a PoS network
*/ */
void setSafeBlock(Hash blockHash) throws IllegalArgumentException, UnsupportedOperationException; void setSafeBlock(Hash blockHash) throws IllegalArgumentException, UnsupportedOperationException;
;
/**
* Get the chain id
*
* @return the chain id
*/
Optional<BigInteger> getChainId();
} }

Loading…
Cancel
Save