Merge branch 'unstable' into merge-unstable-to-deneb-20231005

# Conflicts:
#	.github/workflows/test-suite.yml
#	Cargo.lock
#	beacon_node/execution_layer/Cargo.toml
#	beacon_node/execution_layer/src/test_utils/mock_builder.rs
#	beacon_node/execution_layer/src/test_utils/mod.rs
#	beacon_node/network/src/service/tests.rs
#	consensus/types/src/builder_bid.rs
This commit is contained in:
Jimmy Chen 2023-10-05 15:21:18 +11:00
commit c5c84f1213
No known key found for this signature in database
GPG Key ID: 7AAEE02659DCF690
48 changed files with 1791 additions and 1491 deletions

113
.config/nextest.toml Normal file
View File

@ -0,0 +1,113 @@
# This is the default config used by nextest. It is embedded in the binary at
# build time. It may be used as a template for .config/nextest.toml.
[store]
# The directory under the workspace root at which nextest-related files are
# written. Profile-specific storage is currently written to dir/<profile-name>.
dir = "target/nextest"
# This section defines the default nextest profile. Custom profiles are layered
# on top of the default profile.
[profile.default]
# "retries" defines the number of times a test should be retried. If set to a
# non-zero value, tests that succeed on a subsequent attempt will be marked as
# non-flaky. Can be overridden through the `--retries` option.
# Examples
# * retries = 3
# * retries = { backoff = "fixed", count = 2, delay = "1s" }
# * retries = { backoff = "exponential", count = 10, delay = "1s", jitter = true, max-delay = "10s" }
retries = 0
# The number of threads to run tests with. Supported values are either an integer or
# the string "num-cpus". Can be overridden through the `--test-threads` option.
test-threads = "num-cpus"
# The number of threads required for each test. This is generally used in overrides to
# mark certain tests as heavier than others. However, it can also be set as a global parameter.
threads-required = 1
# Show these test statuses in the output.
#
# The possible values this can take are:
# * none: no output
# * fail: show failed (including exec-failed) tests
# * retry: show flaky and retried tests
# * slow: show slow tests
# * pass: show passed tests
# * skip: show skipped tests (most useful for CI)
# * all: all of the above
#
# Each value includes all the values above it; for example, "slow" includes
# failed and retried tests.
#
# Can be overridden through the `--status-level` flag.
status-level = "pass"
# Similar to status-level, show these test statuses at the end of the run.
final-status-level = "flaky"
# "failure-output" defines when standard output and standard error for failing tests are produced.
# Accepted values are
# * "immediate": output failures as soon as they happen
# * "final": output failures at the end of the test run
# * "immediate-final": output failures as soon as they happen and at the end of
# the test run; combination of "immediate" and "final"
# * "never": don't output failures at all
#
# For large test suites and CI it is generally useful to use "immediate-final".
#
# Can be overridden through the `--failure-output` option.
failure-output = "immediate"
# "success-output" controls production of standard output and standard error on success. This should
# generally be set to "never".
success-output = "never"
# Cancel the test run on the first failure. For CI runs, consider setting this
# to false.
fail-fast = true
# Treat a test that takes longer than the configured 'period' as slow, and print a message.
# See <https://nexte.st/book/slow-tests> for more information.
#
# Optional: specify the parameter 'terminate-after' with a non-zero integer,
# which will cause slow tests to be terminated after the specified number of
# periods have passed.
# Example: slow-timeout = { period = "60s", terminate-after = 2 }
slow-timeout = { period = "120s" }
# Treat a test as leaky if after the process is shut down, standard output and standard error
# aren't closed within this duration.
#
# This usually happens in case of a test that creates a child process and lets it inherit those
# handles, but doesn't clean the child process up (especially when it fails).
#
# See <https://nexte.st/book/leaky-tests> for more information.
leak-timeout = "100ms"
[profile.default.junit]
# Output a JUnit report into the given file inside 'store.dir/<profile-name>'.
# If unspecified, JUnit is not written out.
# path = "junit.xml"
# The name of the top-level "report" element in JUnit report. If aggregating
# reports across different test runs, it may be useful to provide separate names
# for each report.
report-name = "lighthouse-run"
# Whether standard output and standard error for passing tests should be stored in the JUnit report.
# Output is stored in the <system-out> and <system-err> elements of the <testcase> element.
store-success-output = false
# Whether standard output and standard error for failing tests should be stored in the JUnit report.
# Output is stored in the <system-out> and <system-err> elements of the <testcase> element.
#
# Note that if a description can be extracted from the output, it is always stored in the
# <description> element.
store-failure-output = true
# This profile is activated if MIRI_SYSROOT is set.
[profile.default-miri]
# Miri tests take up a lot of memory, so only run 1 test at a time by default.
test-threads = 4

View File

@ -18,14 +18,14 @@ env:
# Deny warnings in CI # Deny warnings in CI
# Disable debug info (see https://github.com/sigp/lighthouse/issues/4005) # Disable debug info (see https://github.com/sigp/lighthouse/issues/4005)
RUSTFLAGS: "-D warnings -C debuginfo=0" RUSTFLAGS: "-D warnings -C debuginfo=0"
# The Nightly version used for cargo-udeps, might need updating from time to time.
PINNED_NIGHTLY: nightly-2023-04-16
# Prevent Github API rate limiting. # Prevent Github API rate limiting.
LIGHTHOUSE_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} LIGHTHOUSE_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Enable self-hosted runners for the sigp repo only. # Enable self-hosted runners for the sigp repo only.
SELF_HOSTED_RUNNERS: ${{ github.repository == 'sigp/lighthouse' }} SELF_HOSTED_RUNNERS: ${{ github.repository == 'sigp/lighthouse' }}
# Self-hosted runners need to reference a different host for `./watch` tests. # Self-hosted runners need to reference a different host for `./watch` tests.
WATCH_HOST: ${{ github.repository == 'sigp/lighthouse' && 'host.docker.internal' || 'localhost' }} WATCH_HOST: ${{ github.repository == 'sigp/lighthouse' && 'host.docker.internal' || 'localhost' }}
# Disable incremental compilation
CARGO_INCREMENTAL: 0
jobs: jobs:
target-branch-check: target-branch-check:
name: target-branch-check name: target-branch-check
@ -34,155 +34,191 @@ jobs:
steps: steps:
- name: Check that the pull request is not targeting the stable branch - name: Check that the pull request is not targeting the stable branch
run: test ${{ github.base_ref }} != "stable" run: test ${{ github.base_ref }} != "stable"
extract-msrv:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Extract Minimum Supported Rust Version (MSRV)
run: |
metadata=$(cargo metadata --no-deps --format-version 1)
msrv=$(echo $metadata | jq -r '.packages | map(select(.name == "lighthouse")) | .[0].rust_version')
echo "MSRV=$msrv" >> $GITHUB_OUTPUT
id: extract_msrv
outputs:
MSRV: ${{ steps.extract_msrv.outputs.MSRV }}
cargo-fmt:
name: cargo-fmt
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Get latest version of stable Rust
run: rustup update stable
- name: Check formatting with cargo fmt
run: make cargo-fmt
release-tests-ubuntu: release-tests-ubuntu:
name: release-tests-ubuntu name: release-tests-ubuntu
# Use self-hosted runners only on the sigp repo. # Use self-hosted runners only on the sigp repo.
runs-on: ${{ github.repository == 'sigp/lighthouse' && fromJson('["self-hosted", "linux", "CI", "large"]') || 'ubuntu-latest' }} runs-on: ${{ github.repository == 'sigp/lighthouse' && fromJson('["self-hosted", "linux", "CI", "large"]') || 'ubuntu-latest' }}
needs: cargo-fmt
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Get latest version of stable Rust - name: Get latest version of stable Rust
if: env.SELF_HOSTED_RUNNERS == false if: env.SELF_HOSTED_RUNNERS == 'false'
run: rustup update stable uses: moonrepo/setup-rust@v1
with:
channel: stable
cache-target: release
bins: cargo-nextest
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Install Foundry (anvil) - name: Install Foundry (anvil)
if: env.SELF_HOSTED_RUNNERS == 'false'
uses: foundry-rs/foundry-toolchain@v1 uses: foundry-rs/foundry-toolchain@v1
with: with:
version: nightly-ca67d15f4abd46394b324c50e21e66f306a1162d version: nightly-ca67d15f4abd46394b324c50e21e66f306a1162d
- name: Run tests in release - name: Run tests in release
run: make test-release run: make nextest-release
- name: Show cache stats
if: env.SELF_HOSTED_RUNNERS == 'true'
run: sccache --show-stats
release-tests-windows: release-tests-windows:
name: release-tests-windows name: release-tests-windows
runs-on: ${{ github.repository == 'sigp/lighthouse' && fromJson('["self-hosted", "windows", "CI"]') || 'windows-2019' }} runs-on: ${{ github.repository == 'sigp/lighthouse' && fromJson('["self-hosted", "windows", "CI"]') || 'windows-2019' }}
needs: cargo-fmt
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Get latest version of stable Rust - name: Get latest version of stable Rust
if: env.SELF_HOSTED_RUNNERS == false if: env.SELF_HOSTED_RUNNERS == 'false'
run: rustup update stable uses: moonrepo/setup-rust@v1
with:
channel: stable
cache-target: release
bins: cargo-nextest
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Install Foundry (anvil) - name: Install Foundry (anvil)
if: env.SELF_HOSTED_RUNNERS == 'false'
uses: foundry-rs/foundry-toolchain@v1 uses: foundry-rs/foundry-toolchain@v1
with: with:
version: nightly-ca67d15f4abd46394b324c50e21e66f306a1162d version: nightly-ca67d15f4abd46394b324c50e21e66f306a1162d
- name: Install make - name: Install make
if: env.SELF_HOSTED_RUNNERS == 'false'
run: choco install -y make run: choco install -y make
- uses: KyleMayes/install-llvm-action@v1 # - uses: KyleMayes/install-llvm-action@v1
if: env.SELF_HOSTED_RUNNERS == false # if: env.SELF_HOSTED_RUNNERS == 'false'
with: # with:
version: "15.0" # version: "15.0"
directory: ${{ runner.temp }}/llvm # directory: ${{ runner.temp }}/llvm
- name: Set LIBCLANG_PATH - name: Set LIBCLANG_PATH
run: echo "LIBCLANG_PATH=$((gcm clang).source -replace "clang.exe")" >> $env:GITHUB_ENV run: echo "LIBCLANG_PATH=$((gcm clang).source -replace "clang.exe")" >> $env:GITHUB_ENV
- name: Run tests in release - name: Run tests in release
run: make test-release run: make nextest-release
- name: Show cache stats
if: env.SELF_HOSTED_RUNNERS == 'true'
run: sccache --show-stats
beacon-chain-tests: beacon-chain-tests:
name: beacon-chain-tests name: beacon-chain-tests
# Use self-hosted runners only on the sigp repo. # Use self-hosted runners only on the sigp repo.
runs-on: ${{ github.repository == 'sigp/lighthouse' && fromJson('["self-hosted", "linux", "CI", "large"]') || 'ubuntu-latest' }} runs-on: ${{ github.repository == 'sigp/lighthouse' && fromJson('["self-hosted", "linux", "CI", "large"]') || 'ubuntu-latest' }}
needs: cargo-fmt env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Get latest version of stable Rust - name: Get latest version of stable Rust
if: env.SELF_HOSTED_RUNNERS == false if: env.SELF_HOSTED_RUNNERS == 'false'
run: rustup update stable uses: moonrepo/setup-rust@v1
with:
channel: stable
cache-target: release
bins: cargo-nextest
- name: Run beacon_chain tests for all known forks - name: Run beacon_chain tests for all known forks
run: make test-beacon-chain run: make test-beacon-chain
- name: Show cache stats
if: env.SELF_HOSTED_RUNNERS == 'true'
run: sccache --show-stats
op-pool-tests: op-pool-tests:
name: op-pool-tests name: op-pool-tests
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: cargo-fmt env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Get latest version of stable Rust - name: Get latest version of stable Rust
run: rustup update stable uses: moonrepo/setup-rust@v1
with:
channel: stable
cache-target: release
bins: cargo-nextest
- name: Run operation_pool tests for all known forks - name: Run operation_pool tests for all known forks
run: make test-op-pool run: make test-op-pool
network-tests: network-tests:
name: network-tests name: network-tests
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: cargo-fmt env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Get latest version of stable Rust - name: Get latest version of stable Rust
run: rustup update stable uses: moonrepo/setup-rust@v1
- name: Run network tests for all known forks with:
channel: stable
cache-target: release
bins: cargo-nextest
- name: Run network tests for all known forks
run: make test-network run: make test-network
slasher-tests: slasher-tests:
name: slasher-tests name: slasher-tests
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: cargo-fmt env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Get latest version of stable Rust - name: Get latest version of stable Rust
run: rustup update stable uses: moonrepo/setup-rust@v1
with:
channel: stable
cache-target: release
bins: cargo-nextest
- name: Run slasher tests for all supported backends - name: Run slasher tests for all supported backends
run: make test-slasher run: make test-slasher
debug-tests-ubuntu: debug-tests-ubuntu:
name: debug-tests-ubuntu name: debug-tests-ubuntu
# Use self-hosted runners only on the sigp repo. # Use self-hosted runners only on the sigp repo.
runs-on: ${{ github.repository == 'sigp/lighthouse' && fromJson('["self-hosted", "linux", "CI", "large"]') || 'ubuntu-latest' }} runs-on: ${{ github.repository == 'sigp/lighthouse' && fromJson('["self-hosted", "linux", "CI", "large"]') || 'ubuntu-latest' }}
needs: cargo-fmt env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Get latest version of stable Rust - name: Get latest version of stable Rust
if: env.SELF_HOSTED_RUNNERS == false if: env.SELF_HOSTED_RUNNERS == 'false'
run: rustup update stable uses: moonrepo/setup-rust@v1
with:
channel: stable
bins: cargo-nextest
- name: Install Foundry (anvil) - name: Install Foundry (anvil)
if: env.SELF_HOSTED_RUNNERS == 'false'
uses: foundry-rs/foundry-toolchain@v1 uses: foundry-rs/foundry-toolchain@v1
with: with:
version: nightly-ca67d15f4abd46394b324c50e21e66f306a1162d version: nightly-ca67d15f4abd46394b324c50e21e66f306a1162d
- name: Run tests in debug - name: Run tests in debug
run: make test-debug run: make nextest-debug
- name: Show cache stats
if: env.SELF_HOSTED_RUNNERS == 'true'
run: sccache --show-stats
state-transition-vectors-ubuntu: state-transition-vectors-ubuntu:
name: state-transition-vectors-ubuntu name: state-transition-vectors-ubuntu
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: cargo-fmt
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Get latest version of stable Rust - name: Get latest version of stable Rust
run: rustup update stable uses: moonrepo/setup-rust@v1
with:
channel: stable
cache-target: release
- name: Run state_transition_vectors in release. - name: Run state_transition_vectors in release.
run: make run-state-transition-tests run: make run-state-transition-tests
ef-tests-ubuntu: ef-tests-ubuntu:
name: ef-tests-ubuntu name: ef-tests-ubuntu
# Use self-hosted runners only on the sigp repo. # Use self-hosted runners only on the sigp repo.
runs-on: ${{ github.repository == 'sigp/lighthouse' && fromJson('["self-hosted", "linux", "CI", "small"]') || 'ubuntu-latest' }} runs-on: ${{ github.repository == 'sigp/lighthouse' && fromJson('["self-hosted", "linux", "CI", "small"]') || 'ubuntu-latest' }}
needs: cargo-fmt env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Get latest version of stable Rust - name: Get latest version of stable Rust
if: env.SELF_HOSTED_RUNNERS == false if: env.SELF_HOSTED_RUNNERS == 'false'
run: rustup update stable uses: moonrepo/setup-rust@v1
with:
channel: stable
cache-target: release
bins: cargo-nextest
- name: Run consensus-spec-tests with blst, milagro and fake_crypto - name: Run consensus-spec-tests with blst, milagro and fake_crypto
run: make test-ef run: make nextest-ef
- name: Show cache stats
if: env.SELF_HOSTED_RUNNERS == 'true'
run: sccache --show-stats
dockerfile-ubuntu: dockerfile-ubuntu:
name: dockerfile-ubuntu name: dockerfile-ubuntu
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: cargo-fmt
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Get latest version of stable Rust
run: rustup update stable
- name: Build the root Dockerfile - name: Build the root Dockerfile
run: docker build --build-arg FEATURES=portable -t lighthouse:local . run: docker build --build-arg FEATURES=portable -t lighthouse:local .
- name: Test the built image - name: Test the built image
@ -190,11 +226,13 @@ jobs:
eth1-simulator-ubuntu: eth1-simulator-ubuntu:
name: eth1-simulator-ubuntu name: eth1-simulator-ubuntu
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: cargo-fmt
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Get latest version of stable Rust - name: Get latest version of stable Rust
run: rustup update stable uses: moonrepo/setup-rust@v1
with:
channel: stable
cache-target: release
- name: Install Foundry (anvil) - name: Install Foundry (anvil)
uses: foundry-rs/foundry-toolchain@v1 uses: foundry-rs/foundry-toolchain@v1
with: with:
@ -204,11 +242,13 @@ jobs:
merge-transition-ubuntu: merge-transition-ubuntu:
name: merge-transition-ubuntu name: merge-transition-ubuntu
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: cargo-fmt
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Get latest version of stable Rust - name: Get latest version of stable Rust
run: rustup update stable uses: moonrepo/setup-rust@v1
with:
channel: stable
cache-target: release
- name: Install Foundry (anvil) - name: Install Foundry (anvil)
uses: foundry-rs/foundry-toolchain@v1 uses: foundry-rs/foundry-toolchain@v1
with: with:
@ -218,21 +258,25 @@ jobs:
no-eth1-simulator-ubuntu: no-eth1-simulator-ubuntu:
name: no-eth1-simulator-ubuntu name: no-eth1-simulator-ubuntu
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: cargo-fmt
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Get latest version of stable Rust - name: Get latest version of stable Rust
run: rustup update stable uses: moonrepo/setup-rust@v1
with:
channel: stable
cache-target: release
- name: Run the beacon chain sim without an eth1 connection - name: Run the beacon chain sim without an eth1 connection
run: cargo run --release --bin simulator no-eth1-sim run: cargo run --release --bin simulator no-eth1-sim
syncing-simulator-ubuntu: syncing-simulator-ubuntu:
name: syncing-simulator-ubuntu name: syncing-simulator-ubuntu
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: cargo-fmt
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Get latest version of stable Rust - name: Get latest version of stable Rust
run: rustup update stable uses: moonrepo/setup-rust@v1
with:
channel: stable
cache-target: release
- name: Install Foundry (anvil) - name: Install Foundry (anvil)
uses: foundry-rs/foundry-toolchain@v1 uses: foundry-rs/foundry-toolchain@v1
with: with:
@ -241,21 +285,27 @@ jobs:
run: cargo run --release --bin simulator syncing-sim run: cargo run --release --bin simulator syncing-sim
doppelganger-protection-test: doppelganger-protection-test:
name: doppelganger-protection-test name: doppelganger-protection-test
runs-on: ubuntu-latest runs-on: ${{ github.repository == 'sigp/lighthouse' && fromJson('["self-hosted", "linux", "CI", "small"]') || 'ubuntu-latest' }}
needs: cargo-fmt
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Get latest version of stable Rust - name: Get latest version of stable Rust
run: rustup update stable if: env.SELF_HOSTED_RUNNERS == 'false'
uses: moonrepo/setup-rust@v1
with:
channel: stable
cache-target: release
- name: Install geth - name: Install geth
if: env.SELF_HOSTED_RUNNERS == 'false'
run: | run: |
sudo add-apt-repository -y ppa:ethereum/ethereum sudo add-apt-repository -y ppa:ethereum/ethereum
sudo apt-get update sudo apt-get update
sudo apt-get install ethereum sudo apt-get install ethereum
- name: Install lighthouse and lcli - name: Install lighthouse
run: | run: |
make make
make install-lcli - name: Install lcli
if: env.SELF_HOSTED_RUNNERS == 'false'
run: make install-lcli
- name: Run the doppelganger protection failure test script - name: Run the doppelganger protection failure test script
run: | run: |
cd scripts/tests cd scripts/tests
@ -267,91 +317,73 @@ jobs:
execution-engine-integration-ubuntu: execution-engine-integration-ubuntu:
name: execution-engine-integration-ubuntu name: execution-engine-integration-ubuntu
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: cargo-fmt
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- uses: actions/setup-go@v3
with:
go-version: '1.20'
- uses: actions/setup-dotnet@v3
with:
dotnet-version: '6.0.201'
- name: Get latest version of stable Rust - name: Get latest version of stable Rust
run: rustup update stable uses: moonrepo/setup-rust@v1
with:
channel: stable
cache-target: release
cache: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Run exec engine integration tests in release - name: Run exec engine integration tests in release
run: make test-exec-engine run: make test-exec-engine
check-benchmarks: check-code:
name: check-benchmarks name: check-code
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: cargo-fmt env:
CARGO_INCREMENTAL: 1
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Get latest version of stable Rust - name: Get latest version of stable Rust
run: rustup update stable uses: moonrepo/setup-rust@v1
- name: Typecheck benchmark code without running it with:
run: make check-benches channel: stable
clippy: cache-target: release
name: clippy components: rustfmt,clippy
runs-on: ubuntu-latest bins: cargo-audit
needs: cargo-fmt - name: Check formatting with cargo fmt
steps: run: make cargo-fmt
- uses: actions/checkout@v3
- name: Get latest version of stable Rust
run: rustup update stable
- name: Lint code for quality and style with Clippy - name: Lint code for quality and style with Clippy
run: make lint run: make lint
- name: Certify Cargo.lock freshness - name: Certify Cargo.lock freshness
run: git diff --exit-code Cargo.lock run: git diff --exit-code Cargo.lock
- name: Typecheck benchmark code without running it
run: make check-benches
- name: Validate state_processing feature arbitrary-fuzz
run: make arbitrary-fuzz
- name: Run cargo audit
run: make audit-CI
# TODO(sean): re-enable this when we can figure it out with c-kzg
# Issue: https://github.com/sigp/lighthouse/issues/4440
# - name: Run cargo vendor to make sure dependencies can be vendored for packaging, reproducibility and archival purpose
# run: CARGO_HOME=$(readlink -f $HOME) make vendor
check-msrv: check-msrv:
name: check-msrv name: check-msrv
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [cargo-fmt, extract-msrv]
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Install Rust @ MSRV (${{ needs.extract-msrv.outputs.MSRV }}) - name: Install Rust at Minimum Supported Rust Version (MSRV)
run: rustup override set ${{ needs.extract-msrv.outputs.MSRV }} run: |
metadata=$(cargo metadata --no-deps --format-version 1)
msrv=$(echo $metadata | jq -r '.packages | map(select(.name == "lighthouse")) | .[0].rust_version')
rustup override set $msrv
- name: Run cargo check - name: Run cargo check
run: cargo check --workspace run: cargo check --workspace
arbitrary-check:
name: arbitrary-check
runs-on: ubuntu-latest
needs: cargo-fmt
steps:
- uses: actions/checkout@v3
- name: Get latest version of stable Rust
run: rustup update stable
- name: Validate state_processing feature arbitrary-fuzz
run: make arbitrary-fuzz
cargo-audit:
name: cargo-audit
runs-on: ubuntu-latest
needs: cargo-fmt
steps:
- uses: actions/checkout@v3
- name: Get latest version of stable Rust
run: rustup update stable
- name: Run cargo audit to identify known security vulnerabilities reported to the RustSec Advisory Database
run: make audit
# TODO(sean): re-enable this when we can figure it out with c-kzg
# Issue: https://github.com/sigp/lighthouse/issues/4440
# cargo-vendor:
# name: cargo-vendor
# runs-on: ubuntu-latest
# needs: cargo-fmt
# steps:
# - uses: actions/checkout@v3
# - name: Run cargo vendor to make sure dependencies can be vendored for packaging, reproducibility and archival purpose
# run: CARGO_HOME=$(readlink -f $HOME) make vendor
cargo-udeps: cargo-udeps:
name: cargo-udeps name: cargo-udeps
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: cargo-fmt
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Install Rust (${{ env.PINNED_NIGHTLY }}) - name: Get latest version of nightly Rust
run: rustup toolchain install $PINNED_NIGHTLY uses: moonrepo/setup-rust@v1
- name: Install cargo-udeps with:
run: cargo install cargo-udeps --locked --force channel: nightly
bins: cargo-udeps
cache: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Create Cargo config dir - name: Create Cargo config dir
run: mkdir -p .cargo run: mkdir -p .cargo
- name: Install custom Cargo config - name: Install custom Cargo config

587
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -137,7 +137,7 @@ r2d2 = "0.8"
rand = "0.8" rand = "0.8"
rayon = "1.7" rayon = "1.7"
regex = "1" regex = "1"
reqwest = { version = "0.11", default-features = false, features = ["blocking", "json", "stream", "rustls-tls"] } reqwest = { version = "0.11", default-features = false, features = ["blocking", "json", "stream", "rustls-tls", "native-tls-vendored"] }
ring = "0.16" ring = "0.16"
rusqlite = { version = "0.28", features = ["bundled"] } rusqlite = { version = "0.28", features = ["bundled"] }
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }
@ -157,7 +157,7 @@ superstruct = "0.6"
syn = "1" syn = "1"
sysinfo = "0.26" sysinfo = "0.26"
tempfile = "3" tempfile = "3"
tokio = { version = "1", features = ["rt-multi-thread", "sync"] } tokio = { version = "1", features = ["rt-multi-thread", "sync", "signal"] }
tokio-stream = { version = "0.1", features = ["sync"] } tokio-stream = { version = "0.1", features = ["sync"] }
tokio-util = { version = "0.6", features = ["codec", "compat", "time"] } tokio-util = { version = "0.6", features = ["codec", "compat", "time"] }
tree_hash = "0.5" tree_hash = "0.5"

View File

@ -108,11 +108,21 @@ build-release-tarballs:
test-release: test-release:
cargo test --workspace --release --exclude ef_tests --exclude beacon_chain --exclude slasher --exclude network cargo test --workspace --release --exclude ef_tests --exclude beacon_chain --exclude slasher --exclude network
# Runs the full workspace tests in **release**, without downloading any additional
# test vectors, using nextest.
nextest-release:
cargo nextest run --workspace --release --exclude ef_tests --exclude beacon_chain --exclude slasher
# Runs the full workspace tests in **debug**, without downloading any additional test # Runs the full workspace tests in **debug**, without downloading any additional test
# vectors. # vectors.
test-debug: test-debug:
cargo test --workspace --exclude ef_tests --exclude beacon_chain --exclude network cargo test --workspace --exclude ef_tests --exclude beacon_chain --exclude network
# Runs the full workspace tests in **debug**, without downloading any additional test
# vectors, using nextest.
nextest-debug:
cargo nextest run --workspace --exclude ef_tests --exclude beacon_chain
# Runs cargo-fmt (linter). # Runs cargo-fmt (linter).
cargo-fmt: cargo-fmt:
cargo fmt --all -- --check cargo fmt --all -- --check
@ -129,17 +139,25 @@ run-ef-tests:
cargo test --release -p ef_tests --features "ef_tests,$(EF_TEST_FEATURES),milagro" cargo test --release -p ef_tests --features "ef_tests,$(EF_TEST_FEATURES),milagro"
./$(EF_TESTS)/check_all_files_accessed.py $(EF_TESTS)/.accessed_file_log.txt $(EF_TESTS)/consensus-spec-tests ./$(EF_TESTS)/check_all_files_accessed.py $(EF_TESTS)/.accessed_file_log.txt $(EF_TESTS)/consensus-spec-tests
# Runs EF test vectors with nextest
nextest-run-ef-tests:
rm -rf $(EF_TESTS)/.accessed_file_log.txt
cargo nextest run --release -p ef_tests --features "ef_tests,$(EF_TEST_FEATURES)"
cargo nextest run --release -p ef_tests --features "ef_tests,$(EF_TEST_FEATURES),fake_crypto"
cargo nextest run --release -p ef_tests --features "ef_tests,$(EF_TEST_FEATURES),milagro"
./$(EF_TESTS)/check_all_files_accessed.py $(EF_TESTS)/.accessed_file_log.txt $(EF_TESTS)/consensus-spec-tests
# Run the tests in the `beacon_chain` crate for all known forks. # Run the tests in the `beacon_chain` crate for all known forks.
test-beacon-chain: $(patsubst %,test-beacon-chain-%,$(FORKS)) test-beacon-chain: $(patsubst %,test-beacon-chain-%,$(FORKS))
test-beacon-chain-%: test-beacon-chain-%:
env FORK_NAME=$* cargo test --release --features fork_from_env,slasher/lmdb -p beacon_chain env FORK_NAME=$* cargo nextest run --release --features fork_from_env,slasher/lmdb -p beacon_chain
# Run the tests in the `operation_pool` crate for all known forks. # Run the tests in the `operation_pool` crate for all known forks.
test-op-pool: $(patsubst %,test-op-pool-%,$(FORKS)) test-op-pool: $(patsubst %,test-op-pool-%,$(FORKS))
test-op-pool-%: test-op-pool-%:
env FORK_NAME=$* cargo test --release \ env FORK_NAME=$* cargo nextest run --release \
--features 'beacon_chain/fork_from_env'\ --features 'beacon_chain/fork_from_env'\
-p operation_pool -p operation_pool
@ -147,15 +165,15 @@ test-op-pool-%:
test-network: $(patsubst %,test-network-%,$(FORKS)) test-network: $(patsubst %,test-network-%,$(FORKS))
test-network-%: test-network-%:
env FORK_NAME=$* cargo test --release \ env FORK_NAME=$* cargo nextest run --release \
--features 'fork_from_env' \ --features 'fork_from_env' \
-p network -p network
# Run the tests in the `slasher` crate for all supported database backends. # Run the tests in the `slasher` crate for all supported database backends.
test-slasher: test-slasher:
cargo test --release -p slasher --features lmdb cargo nextest run --release -p slasher --features lmdb
cargo test --release -p slasher --no-default-features --features mdbx cargo nextest run --release -p slasher --no-default-features --features mdbx
cargo test --release -p slasher --features lmdb,mdbx # both backends enabled cargo nextest run --release -p slasher --features lmdb,mdbx # both backends enabled
# Runs only the tests/state_transition_vectors tests. # Runs only the tests/state_transition_vectors tests.
run-state-transition-tests: run-state-transition-tests:
@ -164,6 +182,9 @@ run-state-transition-tests:
# Downloads and runs the EF test vectors. # Downloads and runs the EF test vectors.
test-ef: make-ef-tests run-ef-tests test-ef: make-ef-tests run-ef-tests
# Downloads and runs the EF test vectors with nextest.
nextest-ef: make-ef-tests nextest-run-ef-tests
# Runs tests checking interop between Lighthouse and execution clients. # Runs tests checking interop between Lighthouse and execution clients.
test-exec-engine: test-exec-engine:
make -C $(EXECUTION_ENGINE_INTEGRATION) test make -C $(EXECUTION_ENGINE_INTEGRATION) test
@ -213,8 +234,12 @@ arbitrary-fuzz:
cargo check -p slashing_protection --features arbitrary-fuzz cargo check -p slashing_protection --features arbitrary-fuzz
# Runs cargo audit (Audit Cargo.lock files for crates with security vulnerabilities reported to the RustSec Advisory Database) # Runs cargo audit (Audit Cargo.lock files for crates with security vulnerabilities reported to the RustSec Advisory Database)
audit: audit: install-audit audit-CI
install-audit:
cargo install --force cargo-audit cargo install --force cargo-audit
audit-CI:
cargo audit cargo audit
# Runs `cargo vendor` to make sure dependencies can be vendored for packaging, reproducibility and archival purpose. # Runs `cargo vendor` to make sure dependencies can be vendored for packaging, reproducibility and archival purpose.

View File

@ -20,8 +20,8 @@ use execution_layer::test_utils::generate_genesis_header;
use execution_layer::{ use execution_layer::{
auth::JwtKey, auth::JwtKey,
test_utils::{ test_utils::{
ExecutionBlockGenerator, MockBuilder, MockBuilderServer, MockExecutionLayer, ExecutionBlockGenerator, MockBuilder, MockExecutionLayer, DEFAULT_JWT_SECRET,
DEFAULT_JWT_SECRET, DEFAULT_TERMINAL_BLOCK, DEFAULT_TERMINAL_BLOCK,
}, },
ExecutionLayer, ExecutionLayer,
}; };
@ -663,7 +663,10 @@ where
.execution_block_generator() .execution_block_generator()
} }
pub fn set_mock_builder(&mut self, beacon_url: SensitiveUrl) -> MockBuilderServer { pub fn set_mock_builder(
&mut self,
beacon_url: SensitiveUrl,
) -> impl futures::Future<Output = ()> {
let mock_el = self let mock_el = self
.mock_execution_layer .mock_execution_layer
.as_ref() .as_ref()
@ -672,7 +675,7 @@ where
let mock_el_url = SensitiveUrl::parse(mock_el.server.url().as_str()).unwrap(); let mock_el_url = SensitiveUrl::parse(mock_el.server.url().as_str()).unwrap();
// Create the builder, listening on a free port. // Create the builder, listening on a free port.
let (mock_builder, mock_builder_server) = MockBuilder::new_for_testing( let (mock_builder, (addr, mock_builder_server)) = MockBuilder::new_for_testing(
mock_el_url, mock_el_url,
beacon_url, beacon_url,
self.spec.clone(), self.spec.clone(),
@ -680,8 +683,7 @@ where
); );
// Set the builder URL in the execution layer now that its port is known. // Set the builder URL in the execution layer now that its port is known.
let builder_listen_addr = mock_builder_server.local_addr(); let port = addr.port();
let port = builder_listen_addr.port();
mock_el mock_el
.el .el
.set_builder_url( .set_builder_url(

View File

@ -42,12 +42,6 @@ lazy_static = { workspace = true }
ethers-core = { workspace = true } ethers-core = { workspace = true }
builder_client = { path = "../builder_client" } builder_client = { path = "../builder_client" }
fork_choice = { workspace = true } fork_choice = { workspace = true }
#PR: https://github.com/ralexstokes/mev-rs/pull/124
mev-rs = { git = "https://github.com/jimmygchen/mev-rs", rev = "dedc77a" }
axum = "0.6"
hyper = "0.14"
ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev = "12508c1f9b0c8f4bf4c5e9b6d441e840c1b37fd9" }
ssz_rs = "0.9.0"
tokio-stream = { workspace = true } tokio-stream = { workspace = true }
strum = { workspace = true } strum = { workspace = true }
keccak-hash = "0.10.0" keccak-hash = "0.10.0"

View File

@ -1,51 +1,30 @@
use crate::test_utils::{DEFAULT_BUILDER_PAYLOAD_VALUE_WEI, DEFAULT_JWT_SECRET}; use crate::test_utils::{DEFAULT_BUILDER_PAYLOAD_VALUE_WEI, DEFAULT_JWT_SECRET};
use crate::{Config, ExecutionLayer, PayloadAttributes}; use crate::{Config, ExecutionLayer, PayloadAttributes};
use async_trait::async_trait;
use eth2::types::{BlobsBundle, BlockId, StateId, ValidatorId}; use eth2::types::{BlobsBundle, BlockId, StateId, ValidatorId};
use eth2::{BeaconNodeHttpClient, Timeouts}; use eth2::{BeaconNodeHttpClient, Timeouts};
pub use ethereum_consensus::state_transition::Context;
use ethereum_consensus::{
crypto::{SecretKey, Signature},
primitives::{BlsPublicKey, BlsSignature, ExecutionAddress, Hash32, Root, U256},
state_transition::Error,
};
use fork_choice::ForkchoiceUpdateParameters; use fork_choice::ForkchoiceUpdateParameters;
use mev_rs::{
blinded_block_provider::Server as BlindedBlockProviderServer,
signing::{sign_builder_message, verify_signed_builder_message},
types::{
bellatrix::{
BuilderBid as BuilderBidBellatrix, SignedBuilderBid as SignedBuilderBidBellatrix,
},
capella::{BuilderBid as BuilderBidCapella, SignedBuilderBid as SignedBuilderBidCapella},
deneb::{BuilderBid as BuilderBidDeneb, SignedBuilderBid as SignedBuilderBidDeneb},
BidRequest, BuilderBid, ExecutionPayload as ServerPayload, SignedBlindedBeaconBlock,
SignedBuilderBid, SignedValidatorRegistration,
},
Error as MevError,
};
use parking_lot::RwLock; use parking_lot::RwLock;
use sensitive_url::SensitiveUrl; use sensitive_url::SensitiveUrl;
use ssz::{Decode, Encode};
use ssz_rs::{Merkleized, SimpleSerialize};
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt::Debug; use std::fmt::Debug;
use std::net::Ipv4Addr; use std::future::Future;
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use task_executor::TaskExecutor; use task_executor::TaskExecutor;
use tempfile::NamedTempFile; use tempfile::NamedTempFile;
use tree_hash::TreeHash; use tree_hash::TreeHash;
use types::builder_bid::BlindedBlobsBundle; use types::builder_bid::{
BuilderBid, BuilderBidCapella, BuilderBidDeneb, BuilderBidMerge, SignedBuilderBid,
};
use types::{ use types::{
Address, BeaconState, ChainSpec, EthSpec, ExecPayload, ExecutionPayload, Address, BeaconState, ChainSpec, EthSpec, ExecPayload, ExecutionPayload,
ExecutionPayloadHeader, ForkName, ForkVersionedResponse, Hash256, Slot, Uint256, ExecutionPayloadHeaderRefMut, ForkName, ForkVersionedResponse, Hash256, PublicKeyBytes,
Signature, SignedBlindedBeaconBlock, SignedRoot, SignedValidatorRegistrationData, Slot,
Uint256,
}; };
use types::{ExecutionBlockHash, SecretKey};
pub type MockBuilderServer = axum::Server< use warp::{Filter, Rejection};
hyper::server::conn::AddrIncoming,
axum::routing::IntoMakeService<axum::routing::Router>,
>;
#[derive(Clone)] #[derive(Clone)]
pub enum Operation { pub enum Operation {
@ -60,115 +39,156 @@ pub enum Operation {
} }
impl Operation { impl Operation {
fn apply<B: BidStuff>(self, bid: &mut B) -> Result<(), MevError> { fn apply<E: EthSpec, B: BidStuff<E>>(self, bid: &mut B) {
match self { match self {
Operation::FeeRecipient(fee_recipient) => { Operation::FeeRecipient(fee_recipient) => bid.set_fee_recipient(fee_recipient),
*bid.fee_recipient_mut() = to_ssz_rs(&fee_recipient)? Operation::GasLimit(gas_limit) => bid.set_gas_limit(gas_limit as u64),
} Operation::Value(value) => bid.set_value(value),
Operation::GasLimit(gas_limit) => *bid.gas_limit_mut() = gas_limit as u64, Operation::ParentHash(parent_hash) => bid.set_parent_hash(parent_hash),
Operation::Value(value) => *bid.value_mut() = to_ssz_rs(&value)?, Operation::PrevRandao(prev_randao) => bid.set_prev_randao(prev_randao),
Operation::ParentHash(parent_hash) => *bid.parent_hash_mut() = to_ssz_rs(&parent_hash)?, Operation::BlockNumber(block_number) => bid.set_block_number(block_number as u64),
Operation::PrevRandao(prev_randao) => *bid.prev_randao_mut() = to_ssz_rs(&prev_randao)?, Operation::Timestamp(timestamp) => bid.set_timestamp(timestamp as u64),
Operation::BlockNumber(block_number) => *bid.block_number_mut() = block_number as u64, Operation::WithdrawalsRoot(root) => bid.set_withdrawals_root(root),
Operation::Timestamp(timestamp) => *bid.timestamp_mut() = timestamp as u64,
Operation::WithdrawalsRoot(root) => *bid.withdrawals_root_mut()? = to_ssz_rs(&root)?,
} }
Ok(())
} }
} }
#[derive(Debug)]
struct Custom(String);
impl warp::reject::Reject for Custom {}
// contains functions we need for BuilderBids.. not sure what to call this // contains functions we need for BuilderBids.. not sure what to call this
pub trait BidStuff { pub trait BidStuff<E: EthSpec> {
fn fee_recipient_mut(&mut self) -> &mut ExecutionAddress; fn set_fee_recipient(&mut self, fee_recipient_address: Address);
fn gas_limit_mut(&mut self) -> &mut u64; fn set_gas_limit(&mut self, gas_limit: u64);
fn value_mut(&mut self) -> &mut U256; fn set_value(&mut self, value: Uint256);
fn parent_hash_mut(&mut self) -> &mut Hash32; fn set_parent_hash(&mut self, parent_hash: Hash256);
fn prev_randao_mut(&mut self) -> &mut Hash32; fn set_prev_randao(&mut self, randao: Hash256);
fn block_number_mut(&mut self) -> &mut u64; fn set_block_number(&mut self, block_number: u64);
fn timestamp_mut(&mut self) -> &mut u64; fn set_timestamp(&mut self, timestamp: u64);
fn withdrawals_root_mut(&mut self) -> Result<&mut Root, MevError>; fn set_withdrawals_root(&mut self, withdrawals_root: Hash256);
fn sign_builder_message( fn sign_builder_message(&mut self, sk: &SecretKey, spec: &ChainSpec) -> Signature;
&mut self,
signing_key: &SecretKey,
context: &Context,
) -> Result<BlsSignature, Error>;
fn to_signed_bid(self, signature: BlsSignature) -> SignedBuilderBid; fn to_signed_bid(self, signature: Signature) -> SignedBuilderBid<E>;
} }
macro_rules! map_builder_bid { impl<E: EthSpec> BidStuff<E> for BuilderBid<E> {
($self_ident:ident, |$var:ident| $expr:expr) => { fn set_fee_recipient(&mut self, fee_recipient: Address) {
match $self_ident { match self.to_mut().header_mut() {
BuilderBid::Bellatrix($var) => $expr, ExecutionPayloadHeaderRefMut::Merge(header) => {
BuilderBid::Capella($var) => $expr, header.fee_recipient = fee_recipient;
BuilderBid::Deneb($var) => $expr, }
} ExecutionPayloadHeaderRefMut::Capella(header) => {
}; header.fee_recipient = fee_recipient;
} }
ExecutionPayloadHeaderRefMut::Deneb(header) => {
impl BidStuff for BuilderBid { header.fee_recipient = fee_recipient;
fn fee_recipient_mut(&mut self) -> &mut ExecutionAddress { }
map_builder_bid!(self, |bid| &mut bid.header.fee_recipient)
}
fn gas_limit_mut(&mut self) -> &mut u64 {
map_builder_bid!(self, |bid| &mut bid.header.gas_limit)
}
fn value_mut(&mut self) -> &mut U256 {
map_builder_bid!(self, |bid| &mut bid.value)
}
fn parent_hash_mut(&mut self) -> &mut Hash32 {
map_builder_bid!(self, |bid| &mut bid.header.parent_hash)
}
fn prev_randao_mut(&mut self) -> &mut Hash32 {
map_builder_bid!(self, |bid| &mut bid.header.prev_randao)
}
fn block_number_mut(&mut self) -> &mut u64 {
map_builder_bid!(self, |bid| &mut bid.header.block_number)
}
fn timestamp_mut(&mut self) -> &mut u64 {
map_builder_bid!(self, |bid| &mut bid.header.timestamp)
}
fn withdrawals_root_mut(&mut self) -> Result<&mut Root, MevError> {
match self {
Self::Bellatrix(_) => Err(MevError::InvalidFork),
Self::Capella(bid) => Ok(&mut bid.header.withdrawals_root),
Self::Deneb(bid) => Ok(&mut bid.header.withdrawals_root),
} }
} }
fn sign_builder_message( fn set_gas_limit(&mut self, gas_limit: u64) {
&mut self, match self.to_mut().header_mut() {
signing_key: &SecretKey, ExecutionPayloadHeaderRefMut::Merge(header) => {
context: &Context, header.gas_limit = gas_limit;
) -> Result<Signature, Error> { }
map_builder_bid!(self, |message| sign_builder_message( ExecutionPayloadHeaderRefMut::Capella(header) => {
message, header.gas_limit = gas_limit;
signing_key, }
context ExecutionPayloadHeaderRefMut::Deneb(header) => {
)) header.gas_limit = gas_limit;
}
}
} }
fn to_signed_bid(self, signature: Signature) -> SignedBuilderBid { fn set_value(&mut self, value: Uint256) {
match self { *self.value_mut() = value;
Self::Bellatrix(message) => { }
SignedBuilderBid::Bellatrix(SignedBuilderBidBellatrix { message, signature })
fn set_parent_hash(&mut self, parent_hash: Hash256) {
match self.to_mut().header_mut() {
ExecutionPayloadHeaderRefMut::Merge(header) => {
header.parent_hash = ExecutionBlockHash::from_root(parent_hash);
} }
Self::Capella(message) => { ExecutionPayloadHeaderRefMut::Capella(header) => {
SignedBuilderBid::Capella(SignedBuilderBidCapella { message, signature }) header.parent_hash = ExecutionBlockHash::from_root(parent_hash);
} }
Self::Deneb(message) => { ExecutionPayloadHeaderRefMut::Deneb(header) => {
SignedBuilderBid::Deneb(SignedBuilderBidDeneb { message, signature }) header.parent_hash = ExecutionBlockHash::from_root(parent_hash);
} }
} }
} }
fn set_prev_randao(&mut self, prev_randao: Hash256) {
match self.to_mut().header_mut() {
ExecutionPayloadHeaderRefMut::Merge(header) => {
header.prev_randao = prev_randao;
}
ExecutionPayloadHeaderRefMut::Capella(header) => {
header.prev_randao = prev_randao;
}
ExecutionPayloadHeaderRefMut::Deneb(header) => {
header.prev_randao = prev_randao;
}
}
}
fn set_block_number(&mut self, block_number: u64) {
match self.to_mut().header_mut() {
ExecutionPayloadHeaderRefMut::Merge(header) => {
header.block_number = block_number;
}
ExecutionPayloadHeaderRefMut::Capella(header) => {
header.block_number = block_number;
}
ExecutionPayloadHeaderRefMut::Deneb(header) => {
header.block_number = block_number;
}
}
}
fn set_timestamp(&mut self, timestamp: u64) {
match self.to_mut().header_mut() {
ExecutionPayloadHeaderRefMut::Merge(header) => {
header.timestamp = timestamp;
}
ExecutionPayloadHeaderRefMut::Capella(header) => {
header.timestamp = timestamp;
}
ExecutionPayloadHeaderRefMut::Deneb(header) => {
header.timestamp = timestamp;
}
}
}
fn set_withdrawals_root(&mut self, withdrawals_root: Hash256) {
match self.to_mut().header_mut() {
ExecutionPayloadHeaderRefMut::Merge(_) => {
panic!("no withdrawals before capella")
}
ExecutionPayloadHeaderRefMut::Capella(header) => {
header.withdrawals_root = withdrawals_root;
}
ExecutionPayloadHeaderRefMut::Deneb(header) => {
header.withdrawals_root = withdrawals_root;
}
}
}
fn sign_builder_message(&mut self, sk: &SecretKey, spec: &ChainSpec) -> Signature {
let domain = spec.get_builder_domain();
let message = self.signing_root(domain);
sk.sign(message)
}
fn to_signed_bid(self, signature: Signature) -> SignedBuilderBid<E> {
SignedBuilderBid {
message: self,
signature,
}
}
} }
#[derive(Clone)] #[derive(Clone)]
@ -176,8 +196,7 @@ pub struct MockBuilder<E: EthSpec> {
el: ExecutionLayer<E>, el: ExecutionLayer<E>,
beacon_client: BeaconNodeHttpClient, beacon_client: BeaconNodeHttpClient,
spec: ChainSpec, spec: ChainSpec,
context: Arc<Context>, val_registration_cache: Arc<RwLock<HashMap<PublicKeyBytes, SignedValidatorRegistrationData>>>,
val_registration_cache: Arc<RwLock<HashMap<BlsPublicKey, SignedValidatorRegistration>>>,
builder_sk: SecretKey, builder_sk: SecretKey,
operations: Arc<RwLock<Vec<Operation>>>, operations: Arc<RwLock<Vec<Operation>>>,
invalidate_signatures: Arc<RwLock<bool>>, invalidate_signatures: Arc<RwLock<bool>>,
@ -189,7 +208,7 @@ impl<E: EthSpec> MockBuilder<E> {
beacon_url: SensitiveUrl, beacon_url: SensitiveUrl,
spec: ChainSpec, spec: ChainSpec,
executor: TaskExecutor, executor: TaskExecutor,
) -> (Self, MockBuilderServer) { ) -> (Self, (SocketAddr, impl Future<Output = ()>)) {
let file = NamedTempFile::new().unwrap(); let file = NamedTempFile::new().unwrap();
let path = file.path().into(); let path = file.path().into();
std::fs::write(&path, hex::encode(DEFAULT_JWT_SECRET)).unwrap(); std::fs::write(&path, hex::encode(DEFAULT_JWT_SECRET)).unwrap();
@ -205,23 +224,14 @@ impl<E: EthSpec> MockBuilder<E> {
let el = let el =
ExecutionLayer::from_config(config, executor.clone(), executor.log().clone()).unwrap(); ExecutionLayer::from_config(config, executor.clone(), executor.log().clone()).unwrap();
// This should probably be done for all fields, we only update ones we are testing with so far.
let mut context = Context::for_mainnet();
context.terminal_total_difficulty = to_ssz_rs(&spec.terminal_total_difficulty).unwrap();
context.terminal_block_hash = to_ssz_rs(&spec.terminal_block_hash).unwrap();
context.terminal_block_hash_activation_epoch =
to_ssz_rs(&spec.terminal_block_hash_activation_epoch).unwrap();
let builder = MockBuilder::new( let builder = MockBuilder::new(
el, el,
BeaconNodeHttpClient::new(beacon_url, Timeouts::set_all(Duration::from_secs(1))), BeaconNodeHttpClient::new(beacon_url, Timeouts::set_all(Duration::from_secs(1))),
spec, spec,
context,
); );
let host: Ipv4Addr = Ipv4Addr::LOCALHOST; let host: Ipv4Addr = Ipv4Addr::LOCALHOST;
let port = 0; let port = 0;
let provider = BlindedBlockProviderServer::new(host, port, builder.clone()); let server = serve(host, port, builder.clone()).expect("mock builder server should start");
let server = provider.serve();
(builder, server) (builder, server)
} }
@ -229,15 +239,13 @@ impl<E: EthSpec> MockBuilder<E> {
el: ExecutionLayer<E>, el: ExecutionLayer<E>,
beacon_client: BeaconNodeHttpClient, beacon_client: BeaconNodeHttpClient,
spec: ChainSpec, spec: ChainSpec,
context: Context,
) -> Self { ) -> Self {
let sk = SecretKey::random(&mut rand::thread_rng()).unwrap(); let sk = SecretKey::random();
Self { Self {
el, el,
beacon_client, beacon_client,
// Should keep spec and context consistent somehow // Should keep spec and context consistent somehow
spec, spec,
context: Arc::new(context),
val_registration_cache: Arc::new(RwLock::new(HashMap::new())), val_registration_cache: Arc::new(RwLock::new(HashMap::new())),
builder_sk: sk, builder_sk: sk,
operations: Arc::new(RwLock::new(vec![])), operations: Arc::new(RwLock::new(vec![])),
@ -259,296 +267,335 @@ impl<E: EthSpec> MockBuilder<E> {
*self.invalidate_signatures.write() = false; *self.invalidate_signatures.write() = false;
} }
fn apply_operations<B: BidStuff>(&self, bid: &mut B) -> Result<(), MevError> { fn apply_operations<B: BidStuff<E>>(&self, bid: &mut B) {
let mut guard = self.operations.write(); let mut guard = self.operations.write();
while let Some(op) = guard.pop() { while let Some(op) = guard.pop() {
op.apply(bid)?; op.apply(bid);
} }
Ok(())
}
pub fn pubkey(&self) -> ethereum_consensus::crypto::PublicKey {
self.builder_sk.public_key()
} }
} }
#[async_trait] pub fn serve<E: EthSpec>(
impl<E: EthSpec> mev_rs::BlindedBlockProvider for MockBuilder<E> { listen_addr: Ipv4Addr,
async fn register_validators( listen_port: u16,
&self, builder: MockBuilder<E>,
registrations: &mut [SignedValidatorRegistration], ) -> Result<(SocketAddr, impl Future<Output = ()>), crate::test_utils::Error> {
) -> Result<(), MevError> { let inner_ctx = builder.clone();
for registration in registrations { let ctx_filter = warp::any().map(move || inner_ctx.clone());
let pubkey = registration.message.public_key.clone();
let message = &mut registration.message;
verify_signed_builder_message(
message,
&registration.signature,
&pubkey,
&self.context,
)?;
self.val_registration_cache.write().insert(
registration.message.public_key.clone(),
registration.clone(),
);
}
Ok(()) let prefix = warp::path("eth")
} .and(warp::path("v1"))
.and(warp::path("builder"));
async fn fetch_best_bid(&self, bid_request: &BidRequest) -> Result<SignedBuilderBid, MevError> { let validators = prefix
let slot = Slot::new(bid_request.slot); .and(warp::path("validators"))
let fork = self.spec.fork_name_at_slot::<E>(slot); .and(warp::body::json())
let signed_cached_data = self .and(warp::path::end())
.val_registration_cache .and(ctx_filter.clone())
.read() .and_then(
.get(&bid_request.public_key) |registrations: Vec<SignedValidatorRegistrationData>, builder: MockBuilder<E>| async move {
.ok_or_else(|| convert_err("missing registration"))? for registration in registrations {
.clone(); if !registration.verify_signature(&builder.spec) {
let cached_data = signed_cached_data.message; return Err(reject("invalid signature"));
}
builder
.val_registration_cache
.write()
.insert(registration.message.pubkey, registration);
}
Ok(warp::reply())
},
);
let head = self let blinded_block = prefix
.beacon_client .and(warp::path("blinded_blocks"))
.get_beacon_blocks::<E>(BlockId::Head) .and(warp::body::json())
.await .and(warp::path::end())
.map_err(convert_err)? .and(ctx_filter.clone())
.ok_or_else(|| convert_err("missing head block"))?; .and_then(
|block: SignedBlindedBeaconBlock<E>, builder: MockBuilder<E>| async move {
let slot = block.slot();
let root = match block {
SignedBlindedBeaconBlock::Base(_) | types::SignedBeaconBlock::Altair(_) => {
return Err(reject("invalid fork"));
}
SignedBlindedBeaconBlock::Merge(block) => {
block.message.body.execution_payload.tree_hash_root()
}
SignedBlindedBeaconBlock::Capella(block) => {
block.message.body.execution_payload.tree_hash_root()
}
SignedBlindedBeaconBlock::Deneb(block) => {
block.message.body.execution_payload.tree_hash_root()
}
};
let block = head.data.message(); let fork_name = builder.spec.fork_name_at_slot::<E>(slot);
let head_block_root = block.tree_hash_root(); let payload = builder
let head_execution_hash = block .el
.body() .get_payload_by_root(&root)
.execution_payload() .ok_or_else(|| reject("missing payload for tx root"))?;
.map_err(convert_err)? let resp = ForkVersionedResponse {
.block_hash(); version: Some(fork_name),
if head_execution_hash != from_ssz_rs(&bid_request.parent_hash)? { data: payload,
return Err(custom_err(format!( };
"head mismatch: {} {}",
head_execution_hash, bid_request.parent_hash
)));
}
let finalized_execution_hash = self let json_payload = serde_json::to_string(&resp)
.beacon_client .map_err(|_| reject("coudn't serialize response"))?;
.get_beacon_blocks::<E>(BlockId::Finalized) Ok::<_, warp::reject::Rejection>(
.await warp::http::Response::builder()
.map_err(convert_err)? .status(200)
.ok_or_else(|| convert_err("missing finalized block"))? .body(
.data serde_json::to_string(&json_payload)
.message() .map_err(|_| reject("nvalid JSON"))?,
.body() )
.execution_payload() .unwrap(),
.map_err(convert_err)? )
.block_hash(); },
);
let justified_execution_hash = self let status = prefix
.beacon_client .and(warp::path("status"))
.get_beacon_blocks::<E>(BlockId::Justified) .then(|| async { warp::reply() });
.await
.map_err(convert_err)?
.ok_or_else(|| convert_err("missing finalized block"))?
.data
.message()
.body()
.execution_payload()
.map_err(convert_err)?
.block_hash();
let val_index = self let header = prefix
.beacon_client .and(warp::path("header"))
.get_beacon_states_validator_id( .and(warp::path::param::<Slot>().or_else(|_| async { Err(reject("Invalid slot")) }))
StateId::Head, .and(
&ValidatorId::PublicKey(from_ssz_rs(&cached_data.public_key)?), warp::path::param::<ExecutionBlockHash>()
) .or_else(|_| async { Err(reject("Invalid parent hash")) }),
.await )
.map_err(convert_err)? .and(
.ok_or_else(|| convert_err("missing validator from state"))? warp::path::param::<PublicKeyBytes>()
.data .or_else(|_| async { Err(reject("Invalid pubkey")) }),
.index; )
let fee_recipient = from_ssz_rs(&cached_data.fee_recipient)?; .and(warp::path::end())
let slots_since_genesis = slot.as_u64() - self.spec.genesis_slot.as_u64(); .and(ctx_filter.clone())
.and_then(
|slot: Slot,
parent_hash: ExecutionBlockHash,
pubkey: PublicKeyBytes,
builder: MockBuilder<E>| async move {
let fork = builder.spec.fork_name_at_slot::<E>(slot);
let signed_cached_data = builder
.val_registration_cache
.read()
.get(&pubkey)
.ok_or_else(|| reject("missing registration"))?
.clone();
let cached_data = signed_cached_data.message;
let genesis_time = self let head = builder
.beacon_client .beacon_client
.get_beacon_genesis() .get_beacon_blocks::<E>(BlockId::Head)
.await
.map_err(convert_err)?
.data
.genesis_time;
let timestamp = (slots_since_genesis * self.spec.seconds_per_slot) + genesis_time;
let head_state: BeaconState<E> = self
.beacon_client
.get_debug_beacon_states(StateId::Head)
.await
.map_err(convert_err)?
.ok_or_else(|| custom_err("missing head state".to_string()))?
.data;
let prev_randao = head_state
.get_randao_mix(head_state.current_epoch())
.map_err(convert_err)?;
let expected_withdrawals = match fork {
ForkName::Base | ForkName::Altair | ForkName::Merge => None,
ForkName::Capella | ForkName::Deneb => Some(
self.beacon_client
.get_expected_withdrawals(&StateId::Head)
.await .await
.unwrap() .map_err(|_| reject("couldn't get head"))?
.data, .ok_or_else(|| reject("missing head block"))?;
),
};
let payload_attributes = match fork { let block = head.data.message();
// the withdrawals root is filled in by operations, but we supply the valid withdrawals let head_block_root = block.tree_hash_root();
// first to avoid polluting the execution block generator with invalid payload attributes let head_execution_hash = block
// NOTE: this was part of an effort to add payload attribute uniqueness checks, .body()
// which was abandoned because it broke too many tests in subtle ways. .execution_payload()
ForkName::Merge | ForkName::Capella => PayloadAttributes::new( .map_err(|_| reject("pre-merge block"))?
timestamp, .block_hash();
*prev_randao, if head_execution_hash != parent_hash {
fee_recipient, return Err(reject("head mismatch"));
expected_withdrawals, }
None,
),
ForkName::Deneb => PayloadAttributes::new(
timestamp,
*prev_randao,
fee_recipient,
expected_withdrawals,
Some(head_block_root),
),
ForkName::Base | ForkName::Altair => {
return Err(MevError::InvalidFork);
}
};
self.el let finalized_execution_hash = builder
.insert_proposer(slot, head_block_root, val_index, payload_attributes.clone()) .beacon_client
.await; .get_beacon_blocks::<E>(BlockId::Finalized)
.await
.map_err(|_| reject("couldn't get finalized block"))?
.ok_or_else(|| reject("missing finalized block"))?
.data
.message()
.body()
.execution_payload()
.map_err(|_| reject("pre-merge block"))?
.block_hash();
let forkchoice_update_params = ForkchoiceUpdateParameters { let justified_execution_hash = builder
head_root: Hash256::zero(), .beacon_client
head_hash: None, .get_beacon_blocks::<E>(BlockId::Justified)
justified_hash: Some(justified_execution_hash), .await
finalized_hash: Some(finalized_execution_hash), .map_err(|_| reject("couldn't get justified block"))?
}; .ok_or_else(|| reject("missing justified block"))?
.data
.message()
.body()
.execution_payload()
.map_err(|_| reject("pre-merge block"))?
.block_hash();
let (payload, _block_value, maybe_blobs_bundle): ( let val_index = builder
ExecutionPayload<E>, .beacon_client
Uint256, .get_beacon_states_validator_id(StateId::Head, &ValidatorId::PublicKey(pubkey))
Option<BlobsBundle<E>>, .await
) = self .map_err(|_| reject("couldn't get validator"))?
.el .ok_or_else(|| reject("missing validator"))?
.get_full_payload_caching( .data
head_execution_hash, .index;
&payload_attributes, let fee_recipient = cached_data.fee_recipient;
forkchoice_update_params, let slots_since_genesis = slot.as_u64() - builder.spec.genesis_slot.as_u64();
fork,
)
.await
.map_err(convert_err)?
.into();
let header = match payload { let genesis_data = builder
ExecutionPayload::Merge(payload) => ExecutionPayloadHeader::Merge((&payload).into()), .beacon_client
ExecutionPayload::Capella(payload) => { .get_beacon_genesis()
ExecutionPayloadHeader::Capella((&payload).into()) .await
} .map_err(|_| reject("couldn't get beacon genesis"))?
ExecutionPayload::Deneb(payload) => ExecutionPayloadHeader::Deneb((&payload).into()), .data;
}; let genesis_time = genesis_data.genesis_time;
let timestamp =
(slots_since_genesis * builder.spec.seconds_per_slot) + genesis_time;
let mut message = match fork { let head_state: BeaconState<E> = builder
ForkName::Deneb => { .beacon_client
let blinded_blobs: BlindedBlobsBundle<E> = .get_debug_beacon_states(StateId::Head)
maybe_blobs_bundle.map(Into::into).unwrap_or_default(); .await
BuilderBid::Deneb(BuilderBidDeneb { .map_err(|_| reject("couldn't get state"))?
header: to_ssz_rs(&header)?, .ok_or_else(|| reject("missing state"))?
blinded_blobs_bundle: to_ssz_rs(&blinded_blobs)?, .data;
value: to_ssz_rs(&Uint256::from(DEFAULT_BUILDER_PAYLOAD_VALUE_WEI))?, let prev_randao = head_state
public_key: self.builder_sk.public_key(), .get_randao_mix(head_state.current_epoch())
}) .map_err(|_| reject("couldn't get prev randao"))?;
} let expected_withdrawals = match fork {
ForkName::Capella => BuilderBid::Capella(BuilderBidCapella { ForkName::Base | ForkName::Altair | ForkName::Merge => None,
header: to_ssz_rs(&header)?, ForkName::Capella | ForkName::Deneb => Some(
value: to_ssz_rs(&Uint256::from(DEFAULT_BUILDER_PAYLOAD_VALUE_WEI))?, builder
public_key: self.builder_sk.public_key(), .beacon_client
}), .get_expected_withdrawals(&StateId::Head)
ForkName::Merge => BuilderBid::Bellatrix(BuilderBidBellatrix { .await
header: to_ssz_rs(&header)?, .unwrap()
value: to_ssz_rs(&Uint256::from(DEFAULT_BUILDER_PAYLOAD_VALUE_WEI))?, .data,
public_key: self.builder_sk.public_key(), ),
}), };
ForkName::Base | ForkName::Altair => return Err(MevError::InvalidFork),
};
*message.gas_limit_mut() = cached_data.gas_limit;
self.apply_operations(&mut message)?; let payload_attributes = match fork {
let mut signature = // the withdrawals root is filled in by operations, but we supply the valid withdrawals
message.sign_builder_message(&self.builder_sk, self.context.as_ref())?; // first to avoid polluting the execution block generator with invalid payload attributes
// NOTE: this was part of an effort to add payload attribute uniqueness checks,
// which was abandoned because it broke too many tests in subtle ways.
ForkName::Merge | ForkName::Capella => PayloadAttributes::new(
timestamp,
*prev_randao,
fee_recipient,
expected_withdrawals,
None,
),
ForkName::Deneb => PayloadAttributes::new(
timestamp,
*prev_randao,
fee_recipient,
expected_withdrawals,
Some(head_block_root),
),
ForkName::Base | ForkName::Altair => {
return Err(reject("invalid fork"));
}
};
if *self.invalidate_signatures.read() { builder
signature = Signature::default(); .el
} .insert_proposer(slot, head_block_root, val_index, payload_attributes.clone())
.await;
Ok(message.to_signed_bid(signature)) let forkchoice_update_params = ForkchoiceUpdateParameters {
} head_root: Hash256::zero(),
head_hash: None,
justified_hash: Some(justified_execution_hash),
finalized_hash: Some(finalized_execution_hash),
};
async fn open_bid( let (payload, _block_value, maybe_blobs_bundle): (
&self, ExecutionPayload<E>,
signed_block: &mut SignedBlindedBeaconBlock, Uint256,
) -> Result<ServerPayload, MevError> { Option<BlobsBundle<E>>,
let node = match signed_block { ) = builder
SignedBlindedBeaconBlock::Bellatrix(block) => { .el
block.message.body.execution_payload_header.hash_tree_root() .get_full_payload_caching(
} head_execution_hash,
SignedBlindedBeaconBlock::Capella(block) => { &payload_attributes,
block.message.body.execution_payload_header.hash_tree_root() forkchoice_update_params,
} fork,
SignedBlindedBeaconBlock::Deneb(block_and_blobs) => block_and_blobs )
.signed_blinded_block .await
.message .map_err(|_| reject("couldn't get payload"))?
.body .into();
.execution_payload_header
.hash_tree_root(),
}
.map_err(convert_err)?;
let payload = self let mut message = match fork {
.el ForkName::Deneb => BuilderBid::Deneb(BuilderBidDeneb {
.get_payload_by_root(&from_ssz_rs(&node)?) header: payload
.ok_or_else(|| convert_err("missing payload for tx root"))?; .as_deneb()
.map_err(|_| reject("incorrect payload variant"))?
.into(),
blinded_blobs_bundle: maybe_blobs_bundle
.map(Into::into)
.unwrap_or_default(),
value: Uint256::from(DEFAULT_BUILDER_PAYLOAD_VALUE_WEI),
pubkey: builder.builder_sk.public_key().compress(),
}),
ForkName::Capella => BuilderBid::Capella(BuilderBidCapella {
header: payload
.as_capella()
.map_err(|_| reject("incorrect payload variant"))?
.into(),
value: Uint256::from(DEFAULT_BUILDER_PAYLOAD_VALUE_WEI),
pubkey: builder.builder_sk.public_key().compress(),
}),
ForkName::Merge => BuilderBid::Merge(BuilderBidMerge {
header: payload
.as_merge()
.map_err(|_| reject("incorrect payload variant"))?
.into(),
value: Uint256::from(DEFAULT_BUILDER_PAYLOAD_VALUE_WEI),
pubkey: builder.builder_sk.public_key().compress(),
}),
ForkName::Base | ForkName::Altair => return Err(reject("invalid fork")),
};
let fork = payload.payload_ref().fork_name(); message.set_gas_limit(cached_data.gas_limit);
let resp = ForkVersionedResponse {
version: Some(fork),
data: payload,
};
let json_payload = serde_json::to_string(&resp).map_err(convert_err)?; builder.apply_operations(&mut message);
serde_json::from_str(json_payload.as_str()).map_err(convert_err)
} let mut signature =
message.sign_builder_message(&builder.builder_sk, &builder.spec);
if *builder.invalidate_signatures.read() {
signature = Signature::empty();
}
let fork_name = builder
.spec
.fork_name_at_epoch(slot.epoch(E::slots_per_epoch()));
let signed_bid = SignedBuilderBid { message, signature };
let resp = ForkVersionedResponse {
version: Some(fork_name),
data: signed_bid,
};
let json_bid = serde_json::to_string(&resp)
.map_err(|_| reject("coudn't serialize signed bid"))?;
Ok::<_, Rejection>(
warp::http::Response::builder()
.status(200)
.body(json_bid)
.unwrap(),
)
},
);
let routes = warp::post()
.and(validators.or(blinded_block))
.or(warp::get().and(status).or(header))
.map(|reply| warp::reply::with_header(reply, "Server", "lighthouse-mock-builder-server"));
let (listening_socket, server) = warp::serve(routes)
.try_bind_ephemeral(SocketAddrV4::new(listen_addr, listen_port))
.expect("mock builder server should start");
Ok((listening_socket, server))
} }
pub fn from_ssz_rs<T: SimpleSerialize, U: Decode>(ssz_rs_data: &T) -> Result<U, MevError> { fn reject(msg: &'static str) -> Rejection {
U::from_ssz_bytes( warp::reject::custom(Custom(msg.to_string()))
ssz_rs::serialize(ssz_rs_data)
.map_err(convert_err)?
.as_ref(),
)
.map_err(convert_err)
}
pub fn to_ssz_rs<T: Encode, U: SimpleSerialize>(ssz_data: &T) -> Result<U, MevError> {
ssz_rs::deserialize::<U>(&ssz_data.as_ssz_bytes()).map_err(convert_err)
}
pub fn convert_err<E: Debug>(e: E) -> MevError {
custom_err(format!("{e:?}"))
}
// This is a bit of a hack since the `Custom` variant was removed from `mev_rs::Error`.
pub fn custom_err(s: String) -> MevError {
MevError::Consensus(ethereum_consensus::state_transition::Error::Io(
std::io::Error::new(std::io::ErrorKind::Other, s),
))
} }

View File

@ -29,10 +29,7 @@ pub use execution_block_generator::{
Block, ExecutionBlockGenerator, Block, ExecutionBlockGenerator,
}; };
pub use hook::Hook; pub use hook::Hook;
pub use mock_builder::{ pub use mock_builder::{MockBuilder, Operation};
convert_err, custom_err, from_ssz_rs, to_ssz_rs, Context as MockBuilderContext, MockBuilder,
MockBuilderServer, Operation,
};
pub use mock_execution_layer::MockExecutionLayer; pub use mock_execution_layer::MockExecutionLayer;
pub const DEFAULT_TERMINAL_DIFFICULTY: u64 = 6400; pub const DEFAULT_TERMINAL_DIFFICULTY: u64 = 6400;

View File

@ -1632,13 +1632,19 @@ pub fn serve<T: BeaconChainTypes>(
|block_id: BlockId, |block_id: BlockId,
task_spawner: TaskSpawner<T::EthSpec>, task_spawner: TaskSpawner<T::EthSpec>,
chain: Arc<BeaconChain<T>>| { chain: Arc<BeaconChain<T>>| {
task_spawner.blocking_json_task(Priority::P1, move || { // Prioritise requests for the head block root, as it is used by some VCs (including
let (block, execution_optimistic, finalized) = // the Lighthouse VC) to create sync committee messages.
block_id.blinded_block(&chain)?; let priority = if let BlockId(eth2::types::BlockId::Head) = block_id {
Ok(api_types::GenericResponse::from(api_types::RootData::from( Priority::P0
block.canonical_root(), } else {
)) Priority::P1
.add_execution_optimistic_finalized(execution_optimistic, finalized)) };
task_spawner.blocking_json_task(priority, move || {
let (block_root, execution_optimistic, finalized) = block_id.root(&chain)?;
Ok(
api_types::GenericResponse::from(api_types::RootData::from(block_root))
.add_execution_optimistic_finalized(execution_optimistic, finalized),
)
}) })
}, },
); );

View File

@ -267,11 +267,7 @@ impl ApiTester {
// Start the mock builder service prior to building the chain out. // Start the mock builder service prior to building the chain out.
harness.runtime.task_executor.spawn( harness.runtime.task_executor.spawn(
async move { async move { mock_builder_server.await },
if let Err(e) = mock_builder_server.await {
panic!("error in mock builder server: {e:?}");
}
},
"mock_builder_server", "mock_builder_server",
); );

View File

@ -11,11 +11,17 @@ use libp2p::Multiaddr;
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use sha2::{Digest, Sha256}; use sha2::{Digest, Sha256};
use std::net::{Ipv4Addr, Ipv6Addr}; use std::net::{Ipv4Addr, Ipv6Addr};
use std::num::NonZeroU16;
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use types::{ForkContext, ForkName}; use types::{ForkContext, ForkName};
pub const DEFAULT_IPV4_ADDRESS: Ipv4Addr = Ipv4Addr::UNSPECIFIED;
pub const DEFAULT_TCP_PORT: u16 = 9000u16;
pub const DEFAULT_DISC_PORT: u16 = 9000u16;
pub const DEFAULT_QUIC_PORT: u16 = 9001u16;
/// The cache time is set to accommodate the circulation time of an attestation. /// The cache time is set to accommodate the circulation time of an attestation.
/// ///
/// The p2p spec declares that we accept attestations within the following range: /// The p2p spec declares that we accept attestations within the following range:
@ -59,22 +65,22 @@ pub struct Config {
pub enr_address: (Option<Ipv4Addr>, Option<Ipv6Addr>), pub enr_address: (Option<Ipv4Addr>, Option<Ipv6Addr>),
/// The udp ipv4 port to broadcast to peers in order to reach back for discovery. /// The udp ipv4 port to broadcast to peers in order to reach back for discovery.
pub enr_udp4_port: Option<u16>, pub enr_udp4_port: Option<NonZeroU16>,
/// The quic ipv4 port to broadcast to peers in order to reach back for libp2p services. /// The quic ipv4 port to broadcast to peers in order to reach back for libp2p services.
pub enr_quic4_port: Option<u16>, pub enr_quic4_port: Option<NonZeroU16>,
/// The tcp ipv4 port to broadcast to peers in order to reach back for libp2p services. /// The tcp ipv4 port to broadcast to peers in order to reach back for libp2p services.
pub enr_tcp4_port: Option<u16>, pub enr_tcp4_port: Option<NonZeroU16>,
/// The udp ipv6 port to broadcast to peers in order to reach back for discovery. /// The udp ipv6 port to broadcast to peers in order to reach back for discovery.
pub enr_udp6_port: Option<u16>, pub enr_udp6_port: Option<NonZeroU16>,
/// The tcp ipv6 port to broadcast to peers in order to reach back for libp2p services. /// The tcp ipv6 port to broadcast to peers in order to reach back for libp2p services.
pub enr_tcp6_port: Option<u16>, pub enr_tcp6_port: Option<NonZeroU16>,
/// The quic ipv6 port to broadcast to peers in order to reach back for libp2p services. /// The quic ipv6 port to broadcast to peers in order to reach back for libp2p services.
pub enr_quic6_port: Option<u16>, pub enr_quic6_port: Option<NonZeroU16>,
/// Target number of connected peers. /// Target number of connected peers.
pub target_peers: usize, pub target_peers: usize,
@ -304,10 +310,10 @@ impl Default for Config {
.expect("The total rate limit has been specified"), .expect("The total rate limit has been specified"),
); );
let listen_addresses = ListenAddress::V4(ListenAddr { let listen_addresses = ListenAddress::V4(ListenAddr {
addr: Ipv4Addr::UNSPECIFIED, addr: DEFAULT_IPV4_ADDRESS,
disc_port: 9000, disc_port: DEFAULT_DISC_PORT,
quic_port: 9001, quic_port: DEFAULT_QUIC_PORT,
tcp_port: 9000, tcp_port: DEFAULT_TCP_PORT,
}); });
let discv5_listen_config = let discv5_listen_config =

View File

@ -158,11 +158,11 @@ pub fn create_enr_builder_from_config<T: EnrKey>(
} }
if let Some(udp4_port) = config.enr_udp4_port { if let Some(udp4_port) = config.enr_udp4_port {
builder.udp4(udp4_port); builder.udp4(udp4_port.get());
} }
if let Some(udp6_port) = config.enr_udp6_port { if let Some(udp6_port) = config.enr_udp6_port {
builder.udp6(udp6_port); builder.udp6(udp6_port.get());
} }
if enable_libp2p { if enable_libp2p {
@ -171,35 +171,45 @@ pub fn create_enr_builder_from_config<T: EnrKey>(
// the related fields should only be added when both QUIC and libp2p are enabled // the related fields should only be added when both QUIC and libp2p are enabled
if !config.disable_quic_support { if !config.disable_quic_support {
// If we are listening on ipv4, add the quic ipv4 port. // If we are listening on ipv4, add the quic ipv4 port.
if let Some(quic4_port) = config if let Some(quic4_port) = config.enr_quic4_port.or_else(|| {
.enr_quic4_port config
.or_else(|| config.listen_addrs().v4().map(|v4_addr| v4_addr.quic_port)) .listen_addrs()
{ .v4()
builder.add_value(QUIC_ENR_KEY, &quic4_port); .and_then(|v4_addr| v4_addr.quic_port.try_into().ok())
}) {
builder.add_value(QUIC_ENR_KEY, &quic4_port.get());
} }
// If we are listening on ipv6, add the quic ipv6 port. // If we are listening on ipv6, add the quic ipv6 port.
if let Some(quic6_port) = config if let Some(quic6_port) = config.enr_quic6_port.or_else(|| {
.enr_quic6_port config
.or_else(|| config.listen_addrs().v6().map(|v6_addr| v6_addr.quic_port)) .listen_addrs()
{ .v6()
builder.add_value(QUIC6_ENR_KEY, &quic6_port); .and_then(|v6_addr| v6_addr.quic_port.try_into().ok())
}) {
builder.add_value(QUIC6_ENR_KEY, &quic6_port.get());
} }
} }
// If the ENR port is not set, and we are listening over that ip version, use the listening port instead. // If the ENR port is not set, and we are listening over that ip version, use the listening port instead.
let tcp4_port = config let tcp4_port = config.enr_tcp4_port.or_else(|| {
.enr_tcp4_port config
.or_else(|| config.listen_addrs().v4().map(|v4_addr| v4_addr.tcp_port)); .listen_addrs()
.v4()
.and_then(|v4_addr| v4_addr.tcp_port.try_into().ok())
});
if let Some(tcp4_port) = tcp4_port { if let Some(tcp4_port) = tcp4_port {
builder.tcp4(tcp4_port); builder.tcp4(tcp4_port.get());
} }
let tcp6_port = config let tcp6_port = config.enr_tcp6_port.or_else(|| {
.enr_tcp6_port config
.or_else(|| config.listen_addrs().v6().map(|v6_addr| v6_addr.tcp_port)); .listen_addrs()
.v6()
.and_then(|v6_addr| v6_addr.tcp_port.try_into().ok())
});
if let Some(tcp6_port) = tcp6_port { if let Some(tcp6_port) = tcp6_port {
builder.tcp6(tcp6_port); builder.tcp6(tcp6_port.get());
} }
} }
builder builder

View File

@ -21,10 +21,11 @@ pub use libp2p::identity::{Keypair, PublicKey};
use enr::{ATTESTATION_BITFIELD_ENR_KEY, ETH2_ENR_KEY, SYNC_COMMITTEE_BITFIELD_ENR_KEY}; use enr::{ATTESTATION_BITFIELD_ENR_KEY, ETH2_ENR_KEY, SYNC_COMMITTEE_BITFIELD_ENR_KEY};
use futures::prelude::*; use futures::prelude::*;
use futures::stream::FuturesUnordered; use futures::stream::FuturesUnordered;
use libp2p::multiaddr::Protocol;
use libp2p::swarm::behaviour::{DialFailure, FromSwarm}; use libp2p::swarm::behaviour::{DialFailure, FromSwarm};
use libp2p::swarm::THandlerInEvent; use libp2p::swarm::THandlerInEvent;
pub use libp2p::{ pub use libp2p::{
core::{ConnectedPoint, Multiaddr}, core::{transport::ListenerId, ConnectedPoint, Multiaddr},
identity::PeerId, identity::PeerId,
swarm::{ swarm::{
dummy::ConnectionHandler, ConnectionId, DialError, NetworkBehaviour, NotifyHandler, dummy::ConnectionHandler, ConnectionId, DialError, NetworkBehaviour, NotifyHandler,
@ -77,6 +78,19 @@ pub struct DiscoveredPeers {
pub peers: HashMap<Enr, Option<Instant>>, pub peers: HashMap<Enr, Option<Instant>>,
} }
/// Specifies which port numbers should be modified after start of the discovery service
#[derive(Debug)]
pub struct UpdatePorts {
/// TCP port associated wih IPv4 address (if present)
pub tcp4: bool,
/// TCP port associated wih IPv6 address (if present)
pub tcp6: bool,
/// QUIC port associated wih IPv4 address (if present)
pub quic4: bool,
/// QUIC port associated wih IPv6 address (if present)
pub quic6: bool,
}
#[derive(Clone, PartialEq)] #[derive(Clone, PartialEq)]
struct SubnetQuery { struct SubnetQuery {
subnet: Subnet, subnet: Subnet,
@ -177,12 +191,8 @@ pub struct Discovery<TSpec: EthSpec> {
/// always false. /// always false.
pub started: bool, pub started: bool,
/// This keeps track of whether an external UDP port change should also indicate an internal /// Specifies whether various port numbers should be updated after the discovery service has been started
/// TCP port change. As we cannot detect our external TCP port, we assume that the external UDP update_ports: UpdatePorts,
/// port is also our external TCP port. This assumption only holds if the user has not
/// explicitly set their ENR TCP port via the CLI config. The first indicates tcp4 and the
/// second indicates tcp6.
update_tcp_port: (bool, bool),
/// Logger for the discovery behaviour. /// Logger for the discovery behaviour.
log: slog::Logger, log: slog::Logger,
@ -300,10 +310,12 @@ impl<TSpec: EthSpec> Discovery<TSpec> {
} }
} }
let update_tcp_port = ( let update_ports = UpdatePorts {
config.enr_tcp4_port.is_none(), tcp4: config.enr_tcp4_port.is_none(),
config.enr_tcp6_port.is_none(), tcp6: config.enr_tcp6_port.is_none(),
); quic4: config.enr_quic4_port.is_none(),
quic6: config.enr_quic6_port.is_none(),
};
Ok(Self { Ok(Self {
cached_enrs: LruCache::new(50), cached_enrs: LruCache::new(50),
@ -314,7 +326,7 @@ impl<TSpec: EthSpec> Discovery<TSpec> {
discv5, discv5,
event_stream, event_stream,
started: !config.disable_discovery, started: !config.disable_discovery,
update_tcp_port, update_ports,
log, log,
enr_dir, enr_dir,
}) })
@ -555,8 +567,6 @@ impl<TSpec: EthSpec> Discovery<TSpec> {
if let Ok(node_id) = peer_id_to_node_id(peer_id) { if let Ok(node_id) = peer_id_to_node_id(peer_id) {
// If we could convert this peer id, remove it from the DHT and ban it from discovery. // If we could convert this peer id, remove it from the DHT and ban it from discovery.
self.discv5.ban_node(&node_id, None); self.discv5.ban_node(&node_id, None);
// Remove the node from the routing table.
self.discv5.remove_node(&node_id);
} }
for ip_address in ip_addresses { for ip_address in ip_addresses {
@ -1006,8 +1016,8 @@ impl<TSpec: EthSpec> NetworkBehaviour for Discovery<TSpec> {
// Discv5 will have updated our local ENR. We save the updated version // Discv5 will have updated our local ENR. We save the updated version
// to disk. // to disk.
if (self.update_tcp_port.0 && socket_addr.is_ipv4()) if (self.update_ports.tcp4 && socket_addr.is_ipv4())
|| (self.update_tcp_port.1 && socket_addr.is_ipv6()) || (self.update_ports.tcp6 && socket_addr.is_ipv6())
{ {
// Update the TCP port in the ENR // Update the TCP port in the ENR
self.discv5.update_local_enr_socket(socket_addr, true); self.discv5.update_local_enr_socket(socket_addr, true);
@ -1036,12 +1046,79 @@ impl<TSpec: EthSpec> NetworkBehaviour for Discovery<TSpec> {
FromSwarm::DialFailure(DialFailure { peer_id, error, .. }) => { FromSwarm::DialFailure(DialFailure { peer_id, error, .. }) => {
self.on_dial_failure(peer_id, error) self.on_dial_failure(peer_id, error)
} }
FromSwarm::NewListenAddr(ev) => {
let addr = ev.addr;
let listener_id = ev.listener_id;
trace!(self.log, "Received NewListenAddr event from swarm"; "listener_id" => ?listener_id, "addr" => ?addr);
let mut addr_iter = addr.iter();
let attempt_enr_update = match addr_iter.next() {
Some(Protocol::Ip4(_)) => match (addr_iter.next(), addr_iter.next()) {
(Some(Protocol::Tcp(port)), None) => {
if !self.update_ports.tcp4 {
debug!(self.log, "Skipping ENR update"; "multiaddr" => ?addr);
return;
}
self.update_enr_tcp_port(port)
}
(Some(Protocol::Udp(port)), Some(Protocol::QuicV1)) => {
if !self.update_ports.quic4 {
debug!(self.log, "Skipping ENR update"; "multiaddr" => ?addr);
return;
}
self.update_enr_quic_port(port)
}
_ => {
debug!(self.log, "Encountered unacceptable multiaddr for listening (unsupported transport)"; "addr" => ?addr);
return;
}
},
Some(Protocol::Ip6(_)) => match (addr_iter.next(), addr_iter.next()) {
(Some(Protocol::Tcp(port)), None) => {
if !self.update_ports.tcp6 {
debug!(self.log, "Skipping ENR update"; "multiaddr" => ?addr);
return;
}
self.update_enr_tcp_port(port)
}
(Some(Protocol::Udp(port)), Some(Protocol::QuicV1)) => {
if !self.update_ports.quic6 {
debug!(self.log, "Skipping ENR update"; "multiaddr" => ?addr);
return;
}
self.update_enr_quic_port(port)
}
_ => {
debug!(self.log, "Encountered unacceptable multiaddr for listening (unsupported transport)"; "addr" => ?addr);
return;
}
},
_ => {
debug!(self.log, "Encountered unacceptable multiaddr for listening (no IP)"; "addr" => ?addr);
return;
}
};
let local_enr: Enr = self.discv5.local_enr();
match attempt_enr_update {
Ok(_) => {
info!(self.log, "Updated local ENR"; "enr" => local_enr.to_base64(), "seq" => local_enr.seq(), "id"=> %local_enr.node_id(), "ip4" => ?local_enr.ip4(), "udp4"=> ?local_enr.udp4(), "tcp4" => ?local_enr.tcp4(), "tcp6" => ?local_enr.tcp6(), "udp6" => ?local_enr.udp6())
}
Err(e) => warn!(self.log, "Failed to update ENR"; "error" => ?e),
}
}
FromSwarm::ConnectionEstablished(_) FromSwarm::ConnectionEstablished(_)
| FromSwarm::ConnectionClosed(_) | FromSwarm::ConnectionClosed(_)
| FromSwarm::AddressChange(_) | FromSwarm::AddressChange(_)
| FromSwarm::ListenFailure(_) | FromSwarm::ListenFailure(_)
| FromSwarm::NewListener(_) | FromSwarm::NewListener(_)
| FromSwarm::NewListenAddr(_)
| FromSwarm::ExpiredListenAddr(_) | FromSwarm::ExpiredListenAddr(_)
| FromSwarm::ListenerError(_) | FromSwarm::ListenerError(_)
| FromSwarm::ListenerClosed(_) | FromSwarm::ListenerClosed(_)

View File

@ -415,7 +415,7 @@ impl<TSpec: EthSpec> PeerManager<TSpec> {
/// Reports if a peer is banned or not. /// Reports if a peer is banned or not.
/// ///
/// This is used to determine if we should accept incoming connections. /// This is used to determine if we should accept incoming connections.
pub fn ban_status(&self, peer_id: &PeerId) -> BanResult { pub fn ban_status(&self, peer_id: &PeerId) -> Option<BanResult> {
self.network_globals.peers.read().ban_status(peer_id) self.network_globals.peers.read().ban_status(peer_id)
} }
@ -815,7 +815,7 @@ impl<TSpec: EthSpec> PeerManager<TSpec> {
) -> bool { ) -> bool {
{ {
let mut peerdb = self.network_globals.peers.write(); let mut peerdb = self.network_globals.peers.write();
if !matches!(peerdb.ban_status(peer_id), BanResult::NotBanned) { if peerdb.ban_status(peer_id).is_some() {
// don't connect if the peer is banned // don't connect if the peer is banned
error!(self.log, "Connection has been allowed to a banned peer"; "peer_id" => %peer_id); error!(self.log, "Connection has been allowed to a banned peer"; "peer_id" => %peer_id);
} }

View File

@ -1,5 +1,6 @@
//! Implementation of [`NetworkBehaviour`] for the [`PeerManager`]. //! Implementation of [`NetworkBehaviour`] for the [`PeerManager`].
use std::net::IpAddr;
use std::task::{Context, Poll}; use std::task::{Context, Poll};
use futures::StreamExt; use futures::StreamExt;
@ -8,17 +9,17 @@ use libp2p::identity::PeerId;
use libp2p::swarm::behaviour::{ConnectionClosed, ConnectionEstablished, DialFailure, FromSwarm}; use libp2p::swarm::behaviour::{ConnectionClosed, ConnectionEstablished, DialFailure, FromSwarm};
use libp2p::swarm::dial_opts::{DialOpts, PeerCondition}; use libp2p::swarm::dial_opts::{DialOpts, PeerCondition};
use libp2p::swarm::dummy::ConnectionHandler; use libp2p::swarm::dummy::ConnectionHandler;
use libp2p::swarm::{ConnectionId, NetworkBehaviour, PollParameters, ToSwarm}; use libp2p::swarm::{ConnectionDenied, ConnectionId, NetworkBehaviour, PollParameters, ToSwarm};
use slog::{debug, error}; use slog::{debug, error, trace};
use types::EthSpec; use types::EthSpec;
use crate::discovery::enr_ext::EnrExt; use crate::discovery::enr_ext::EnrExt;
use crate::peer_manager::peerdb::BanResult;
use crate::rpc::GoodbyeReason; use crate::rpc::GoodbyeReason;
use crate::types::SyncState; use crate::types::SyncState;
use crate::{metrics, ClearDialError}; use crate::{metrics, ClearDialError};
use super::peerdb::BanResult; use super::{ConnectingType, PeerManager, PeerManagerEvent};
use super::{ConnectingType, PeerManager, PeerManagerEvent, ReportSource};
impl<TSpec: EthSpec> NetworkBehaviour for PeerManager<TSpec> { impl<TSpec: EthSpec> NetworkBehaviour for PeerManager<TSpec> {
type ConnectionHandler = ConnectionHandler; type ConnectionHandler = ConnectionHandler;
@ -169,26 +170,64 @@ impl<TSpec: EthSpec> NetworkBehaviour for PeerManager<TSpec> {
} }
} }
fn handle_pending_inbound_connection(
&mut self,
_connection_id: ConnectionId,
_local_addr: &libp2p::Multiaddr,
remote_addr: &libp2p::Multiaddr,
) -> Result<(), ConnectionDenied> {
// get the IP address to verify it's not banned.
let ip = match remote_addr.iter().next() {
Some(libp2p::multiaddr::Protocol::Ip6(ip)) => IpAddr::V6(ip),
Some(libp2p::multiaddr::Protocol::Ip4(ip)) => IpAddr::V4(ip),
_ => {
return Err(ConnectionDenied::new(format!(
"Connection to peer rejected: invalid multiaddr: {remote_addr}"
)))
}
};
if self.network_globals.peers.read().is_ip_banned(&ip) {
return Err(ConnectionDenied::new(format!(
"Connection to peer rejected: peer {ip} is banned"
)));
}
Ok(())
}
fn handle_established_inbound_connection( fn handle_established_inbound_connection(
&mut self, &mut self,
_connection_id: ConnectionId, _connection_id: ConnectionId,
_peer: PeerId, peer_id: PeerId,
_local_addr: &libp2p::Multiaddr, _local_addr: &libp2p::Multiaddr,
_remote_addr: &libp2p::Multiaddr, remote_addr: &libp2p::Multiaddr,
) -> Result<libp2p::swarm::THandler<Self>, libp2p::swarm::ConnectionDenied> { ) -> Result<libp2p::swarm::THandler<Self>, ConnectionDenied> {
// TODO: we might want to check if we accept this peer or not in the future. trace!(self.log, "Inbound connection"; "peer_id" => %peer_id, "multiaddr" => %remote_addr);
// We already checked if the peer was banned on `handle_pending_inbound_connection`.
if let Some(BanResult::BadScore) = self.ban_status(&peer_id) {
return Err(ConnectionDenied::new(
"Connection to peer rejected: peer has a bad score",
));
}
Ok(ConnectionHandler) Ok(ConnectionHandler)
} }
fn handle_established_outbound_connection( fn handle_established_outbound_connection(
&mut self, &mut self,
_connection_id: ConnectionId, _connection_id: ConnectionId,
_peer: PeerId, peer_id: PeerId,
_addr: &libp2p::Multiaddr, addr: &libp2p::Multiaddr,
_role_override: libp2p::core::Endpoint, _role_override: libp2p::core::Endpoint,
) -> Result<libp2p::swarm::THandler<Self>, libp2p::swarm::ConnectionDenied> { ) -> Result<libp2p::swarm::THandler<Self>, libp2p::swarm::ConnectionDenied> {
// TODO: we might want to check if we accept this peer or not in the future. trace!(self.log, "Outbound connection"; "peer_id" => %peer_id, "multiaddr" => %addr);
Ok(ConnectionHandler) match self.ban_status(&peer_id) {
Some(cause) => {
error!(self.log, "Connected a banned peer. Rejecting connection"; "peer_id" => %peer_id);
Err(ConnectionDenied::new(cause))
}
None => Ok(ConnectionHandler),
}
} }
} }
@ -215,10 +254,7 @@ impl<TSpec: EthSpec> PeerManager<TSpec> {
// increment prometheus metrics // increment prometheus metrics
if self.metrics_enabled { if self.metrics_enabled {
let remote_addr = match endpoint { let remote_addr = endpoint.get_remote_address();
ConnectedPoint::Dialer { address, .. } => address,
ConnectedPoint::Listener { send_back_addr, .. } => send_back_addr,
};
match remote_addr.iter().find(|proto| { match remote_addr.iter().find(|proto| {
matches!( matches!(
proto, proto,
@ -241,28 +277,6 @@ impl<TSpec: EthSpec> PeerManager<TSpec> {
metrics::inc_counter(&metrics::PEER_CONNECT_EVENT_COUNT); metrics::inc_counter(&metrics::PEER_CONNECT_EVENT_COUNT);
} }
// Check to make sure the peer is not supposed to be banned
match self.ban_status(&peer_id) {
// TODO: directly emit the ban event?
BanResult::BadScore => {
// This is a faulty state
error!(self.log, "Connected to a banned peer. Re-banning"; "peer_id" => %peer_id);
// Disconnect the peer.
self.goodbye_peer(&peer_id, GoodbyeReason::Banned, ReportSource::PeerManager);
// Re-ban the peer to prevent repeated errors.
self.events.push(PeerManagerEvent::Banned(peer_id, vec![]));
return;
}
BanResult::BannedIp(ip_addr) => {
// A good peer has connected to us via a banned IP address. We ban the peer and
// prevent future connections.
debug!(self.log, "Peer connected via banned IP. Banning"; "peer_id" => %peer_id, "banned_ip" => %ip_addr);
self.goodbye_peer(&peer_id, GoodbyeReason::BannedIP, ReportSource::PeerManager);
return;
}
BanResult::NotBanned => {}
}
// Count dialing peers in the limit if the peer dialed us. // Count dialing peers in the limit if the peer dialed us.
let count_dialing = endpoint.is_listener(); let count_dialing = endpoint.is_listener();
// Check the connection limits // Check the connection limits
@ -326,11 +340,7 @@ impl<TSpec: EthSpec> PeerManager<TSpec> {
// reference so that peer manager can track this peer. // reference so that peer manager can track this peer.
self.inject_disconnect(&peer_id); self.inject_disconnect(&peer_id);
let remote_addr = match endpoint { let remote_addr = endpoint.get_remote_address();
ConnectedPoint::Listener { send_back_addr, .. } => send_back_addr,
ConnectedPoint::Dialer { address, .. } => address,
};
// Update the prometheus metrics // Update the prometheus metrics
if self.metrics_enabled { if self.metrics_enabled {
match remote_addr.iter().find(|proto| { match remote_addr.iter().find(|proto| {

View File

@ -3,10 +3,13 @@ use peer_info::{ConnectionDirection, PeerConnectionStatus, PeerInfo};
use rand::seq::SliceRandom; use rand::seq::SliceRandom;
use score::{PeerAction, ReportSource, Score, ScoreState}; use score::{PeerAction, ReportSource, Score, ScoreState};
use slog::{crit, debug, error, trace, warn}; use slog::{crit, debug, error, trace, warn};
use std::cmp::Ordering;
use std::collections::{HashMap, HashSet};
use std::net::IpAddr; use std::net::IpAddr;
use std::time::Instant; use std::time::Instant;
use std::{cmp::Ordering, fmt::Display};
use std::{
collections::{HashMap, HashSet},
fmt::Formatter,
};
use sync_status::SyncStatus; use sync_status::SyncStatus;
use types::EthSpec; use types::EthSpec;
@ -136,26 +139,18 @@ impl<TSpec: EthSpec> PeerDB<TSpec> {
} }
} }
/// Returns the current [`BanResult`] of the peer. This doesn't check the connection state, rather the /// Returns the current [`BanResult`] of the peer if banned. This doesn't check the connection state, rather the
/// underlying score of the peer. A peer may be banned but still in the connected state /// underlying score of the peer. A peer may be banned but still in the connected state
/// temporarily. /// temporarily.
/// ///
/// This is used to determine if we should accept incoming connections or not. /// This is used to determine if we should accept incoming connections or not.
pub fn ban_status(&self, peer_id: &PeerId) -> BanResult { pub fn ban_status(&self, peer_id: &PeerId) -> Option<BanResult> {
if let Some(peer) = self.peers.get(peer_id) { self.peers
match peer.score_state() { .get(peer_id)
ScoreState::Banned => BanResult::BadScore, .and_then(|peer| match peer.score_state() {
_ => { ScoreState::Banned => Some(BanResult::BadScore),
if let Some(ip) = self.ip_is_banned(peer) { _ => self.ip_is_banned(peer).map(BanResult::BannedIp),
BanResult::BannedIp(ip) })
} else {
BanResult::NotBanned
}
}
}
} else {
BanResult::NotBanned
}
} }
/// Checks if the peer's known addresses are currently banned. /// Checks if the peer's known addresses are currently banned.
@ -1183,23 +1178,25 @@ pub enum BanOperation {
} }
/// When checking if a peer is banned, it can be banned for multiple reasons. /// When checking if a peer is banned, it can be banned for multiple reasons.
#[derive(Copy, Clone, Debug)]
pub enum BanResult { pub enum BanResult {
/// The peer's score is too low causing it to be banned. /// The peer's score is too low causing it to be banned.
BadScore, BadScore,
/// The peer should be banned because it is connecting from a banned IP address. /// The peer should be banned because it is connecting from a banned IP address.
BannedIp(IpAddr), BannedIp(IpAddr),
/// The peer is not banned.
NotBanned,
} }
// Helper function for unit tests impl Display for BanResult {
#[cfg(test)] fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
impl BanResult { match self {
pub fn is_banned(&self) -> bool { BanResult::BadScore => write!(f, "Peer has a bad score"),
!matches!(self, BanResult::NotBanned) BanResult::BannedIp(addr) => write!(f, "Peer address: {} is banned", addr),
}
} }
} }
impl std::error::Error for BanResult {}
#[derive(Default)] #[derive(Default)]
pub struct BannedPeersCount { pub struct BannedPeersCount {
/// The number of banned peers in the database. /// The number of banned peers in the database.
@ -1852,11 +1849,11 @@ mod tests {
} }
//check that ip1 and ip2 are banned but ip3-5 not //check that ip1 and ip2 are banned but ip3-5 not
assert!(pdb.ban_status(&p1).is_banned()); assert!(pdb.ban_status(&p1).is_some());
assert!(pdb.ban_status(&p2).is_banned()); assert!(pdb.ban_status(&p2).is_some());
assert!(!pdb.ban_status(&p3).is_banned()); assert!(pdb.ban_status(&p3).is_none());
assert!(!pdb.ban_status(&p4).is_banned()); assert!(pdb.ban_status(&p4).is_none());
assert!(!pdb.ban_status(&p5).is_banned()); assert!(pdb.ban_status(&p5).is_none());
//ban also the last peer in peers //ban also the last peer in peers
let _ = pdb.report_peer( let _ = pdb.report_peer(
@ -1868,11 +1865,11 @@ mod tests {
pdb.inject_disconnect(&peers[BANNED_PEERS_PER_IP_THRESHOLD + 1]); pdb.inject_disconnect(&peers[BANNED_PEERS_PER_IP_THRESHOLD + 1]);
//check that ip1-ip4 are banned but ip5 not //check that ip1-ip4 are banned but ip5 not
assert!(pdb.ban_status(&p1).is_banned()); assert!(pdb.ban_status(&p1).is_some());
assert!(pdb.ban_status(&p2).is_banned()); assert!(pdb.ban_status(&p2).is_some());
assert!(pdb.ban_status(&p3).is_banned()); assert!(pdb.ban_status(&p3).is_some());
assert!(pdb.ban_status(&p4).is_banned()); assert!(pdb.ban_status(&p4).is_some());
assert!(!pdb.ban_status(&p5).is_banned()); assert!(pdb.ban_status(&p5).is_none());
//peers[0] gets unbanned //peers[0] gets unbanned
reset_score(&mut pdb, &peers[0]); reset_score(&mut pdb, &peers[0]);
@ -1880,11 +1877,11 @@ mod tests {
let _ = pdb.shrink_to_fit(); let _ = pdb.shrink_to_fit();
//nothing changed //nothing changed
assert!(pdb.ban_status(&p1).is_banned()); assert!(pdb.ban_status(&p1).is_some());
assert!(pdb.ban_status(&p2).is_banned()); assert!(pdb.ban_status(&p2).is_some());
assert!(pdb.ban_status(&p3).is_banned()); assert!(pdb.ban_status(&p3).is_some());
assert!(pdb.ban_status(&p4).is_banned()); assert!(pdb.ban_status(&p4).is_some());
assert!(!pdb.ban_status(&p5).is_banned()); assert!(pdb.ban_status(&p5).is_none());
//peers[1] gets unbanned //peers[1] gets unbanned
reset_score(&mut pdb, &peers[1]); reset_score(&mut pdb, &peers[1]);
@ -1892,11 +1889,11 @@ mod tests {
let _ = pdb.shrink_to_fit(); let _ = pdb.shrink_to_fit();
//all ips are unbanned //all ips are unbanned
assert!(!pdb.ban_status(&p1).is_banned()); assert!(pdb.ban_status(&p1).is_none());
assert!(!pdb.ban_status(&p2).is_banned()); assert!(pdb.ban_status(&p2).is_none());
assert!(!pdb.ban_status(&p3).is_banned()); assert!(pdb.ban_status(&p3).is_none());
assert!(!pdb.ban_status(&p4).is_banned()); assert!(pdb.ban_status(&p4).is_none());
assert!(!pdb.ban_status(&p5).is_banned()); assert!(pdb.ban_status(&p5).is_none());
} }
#[test] #[test]
@ -1921,8 +1918,8 @@ mod tests {
} }
// check ip is banned // check ip is banned
assert!(pdb.ban_status(&p1).is_banned()); assert!(pdb.ban_status(&p1).is_some());
assert!(!pdb.ban_status(&p2).is_banned()); assert!(pdb.ban_status(&p2).is_none());
// unban a peer // unban a peer
reset_score(&mut pdb, &peers[0]); reset_score(&mut pdb, &peers[0]);
@ -1930,8 +1927,8 @@ mod tests {
let _ = pdb.shrink_to_fit(); let _ = pdb.shrink_to_fit();
// check not banned anymore // check not banned anymore
assert!(!pdb.ban_status(&p1).is_banned()); assert!(pdb.ban_status(&p1).is_none());
assert!(!pdb.ban_status(&p2).is_banned()); assert!(pdb.ban_status(&p2).is_none());
// unban all peers // unban all peers
for p in &peers { for p in &peers {
@ -1950,8 +1947,8 @@ mod tests {
} }
// both IP's are now banned // both IP's are now banned
assert!(pdb.ban_status(&p1).is_banned()); assert!(pdb.ban_status(&p1).is_some());
assert!(pdb.ban_status(&p2).is_banned()); assert!(pdb.ban_status(&p2).is_some());
// unban all peers // unban all peers
for p in &peers { for p in &peers {
@ -1967,16 +1964,16 @@ mod tests {
} }
// nothing is banned // nothing is banned
assert!(!pdb.ban_status(&p1).is_banned()); assert!(pdb.ban_status(&p1).is_none());
assert!(!pdb.ban_status(&p2).is_banned()); assert!(pdb.ban_status(&p2).is_none());
// reban last peer // reban last peer
let _ = pdb.report_peer(&peers[0], PeerAction::Fatal, ReportSource::PeerManager, ""); let _ = pdb.report_peer(&peers[0], PeerAction::Fatal, ReportSource::PeerManager, "");
pdb.inject_disconnect(&peers[0]); pdb.inject_disconnect(&peers[0]);
//Ip's are banned again //Ip's are banned again
assert!(pdb.ban_status(&p1).is_banned()); assert!(pdb.ban_status(&p1).is_some());
assert!(pdb.ban_status(&p2).is_banned()); assert!(pdb.ban_status(&p2).is_some());
} }
#[test] #[test]

View File

@ -20,8 +20,6 @@ where
AppReqId: ReqId, AppReqId: ReqId,
TSpec: EthSpec, TSpec: EthSpec,
{ {
/// Peers banned.
pub banned_peers: libp2p::allow_block_list::Behaviour<libp2p::allow_block_list::BlockedPeers>,
/// Keep track of active and pending connections to enforce hard limits. /// Keep track of active and pending connections to enforce hard limits.
pub connection_limits: libp2p::connection_limits::Behaviour, pub connection_limits: libp2p::connection_limits::Behaviour,
/// The routing pub-sub mechanism for eth2. /// The routing pub-sub mechanism for eth2.

View File

@ -27,6 +27,7 @@ use gossipsub_scoring_parameters::{lighthouse_gossip_thresholds, PeerScoreSettin
use libp2p::bandwidth::BandwidthSinks; use libp2p::bandwidth::BandwidthSinks;
use libp2p::gossipsub::{ use libp2p::gossipsub::{
self, IdentTopic as Topic, MessageAcceptance, MessageAuthenticity, MessageId, PublishError, self, IdentTopic as Topic, MessageAcceptance, MessageAuthenticity, MessageId, PublishError,
TopicScoreParams,
}; };
use libp2p::identify; use libp2p::identify;
use libp2p::multiaddr::{Multiaddr, Protocol as MProtocol}; use libp2p::multiaddr::{Multiaddr, Protocol as MProtocol};
@ -353,11 +354,8 @@ impl<AppReqId: ReqId, TSpec: EthSpec> Network<AppReqId, TSpec> {
libp2p::connection_limits::Behaviour::new(limits) libp2p::connection_limits::Behaviour::new(limits)
}; };
let banned_peers = libp2p::allow_block_list::Behaviour::default();
let behaviour = { let behaviour = {
Behaviour { Behaviour {
banned_peers,
gossipsub, gossipsub,
eth2_rpc, eth2_rpc,
discovery, discovery,
@ -637,6 +635,38 @@ impl<AppReqId: ReqId, TSpec: EthSpec> Network<AppReqId, TSpec> {
} }
} }
/// Remove topic weight from all topics that don't have the given fork digest.
pub fn remove_topic_weight_except(&mut self, except: [u8; 4]) {
let new_param = TopicScoreParams {
topic_weight: 0.0,
..Default::default()
};
let subscriptions = self.network_globals.gossipsub_subscriptions.read().clone();
for topic in subscriptions
.iter()
.filter(|topic| topic.fork_digest != except)
{
let libp2p_topic: Topic = topic.clone().into();
match self
.gossipsub_mut()
.set_topic_params(libp2p_topic, new_param.clone())
{
Ok(_) => debug!(self.log, "Removed topic weight"; "topic" => %topic),
Err(e) => {
warn!(self.log, "Failed to remove topic weight"; "topic" => %topic, "error" => e)
}
}
}
}
/// Returns the scoring parameters for a topic if set.
pub fn get_topic_params(&self, topic: GossipTopic) -> Option<&TopicScoreParams> {
self.swarm
.behaviour()
.gossipsub
.get_topic_params(&topic.into())
}
/// Subscribes to a gossipsub topic. /// Subscribes to a gossipsub topic.
/// ///
/// Returns `true` if the subscription was successful and `false` otherwise. /// Returns `true` if the subscription was successful and `false` otherwise.
@ -1445,15 +1475,10 @@ impl<AppReqId: ReqId, TSpec: EthSpec> Network<AppReqId, TSpec> {
Some(NetworkEvent::PeerDisconnected(peer_id)) Some(NetworkEvent::PeerDisconnected(peer_id))
} }
PeerManagerEvent::Banned(peer_id, associated_ips) => { PeerManagerEvent::Banned(peer_id, associated_ips) => {
self.swarm.behaviour_mut().banned_peers.block_peer(peer_id);
self.discovery_mut().ban_peer(&peer_id, associated_ips); self.discovery_mut().ban_peer(&peer_id, associated_ips);
None None
} }
PeerManagerEvent::UnBanned(peer_id, associated_ips) => { PeerManagerEvent::UnBanned(peer_id, associated_ips) => {
self.swarm
.behaviour_mut()
.banned_peers
.unblock_peer(peer_id);
self.discovery_mut().unban_peer(&peer_id, associated_ips); self.discovery_mut().unban_peer(&peer_id, associated_ips);
None None
} }
@ -1502,7 +1527,6 @@ impl<AppReqId: ReqId, TSpec: EthSpec> Network<AppReqId, TSpec> {
let maybe_event = match swarm_event { let maybe_event = match swarm_event {
SwarmEvent::Behaviour(behaviour_event) => match behaviour_event { SwarmEvent::Behaviour(behaviour_event) => match behaviour_event {
// Handle sub-behaviour events. // Handle sub-behaviour events.
BehaviourEvent::BannedPeers(void) => void::unreachable(void),
BehaviourEvent::Gossipsub(ge) => self.inject_gs_event(ge), BehaviourEvent::Gossipsub(ge) => self.inject_gs_event(ge),
BehaviourEvent::Eth2Rpc(re) => self.inject_rpc_event(re), BehaviourEvent::Eth2Rpc(re) => self.inject_rpc_event(re),
// Inform the peer manager about discovered peers. // Inform the peer manager about discovered peers.

View File

@ -978,9 +978,17 @@ impl<T: BeaconChainTypes> NetworkBeaconProcessor<T> {
self.propagate_validation_result(message_id, peer_id, MessageAcceptance::Ignore); self.propagate_validation_result(message_id, peer_id, MessageAcceptance::Ignore);
return None; return None;
} }
Err(BlockError::BlockIsAlreadyKnown) => {
debug!(
self.log,
"Gossip block is already known";
"block_root" => %block_root,
);
self.propagate_validation_result(message_id, peer_id, MessageAcceptance::Ignore);
return None;
}
Err(e @ BlockError::FutureSlot { .. }) Err(e @ BlockError::FutureSlot { .. })
| Err(e @ BlockError::WouldRevertFinalizedSlot { .. }) | Err(e @ BlockError::WouldRevertFinalizedSlot { .. })
| Err(e @ BlockError::BlockIsAlreadyKnown)
| Err(e @ BlockError::NotFinalizedDescendant { .. }) => { | Err(e @ BlockError::NotFinalizedDescendant { .. }) => {
debug!(self.log, "Could not verify block for gossip. Ignoring the block"; debug!(self.log, "Could not verify block for gossip. Ignoring the block";
"error" => %e); "error" => %e);

View File

@ -215,15 +215,18 @@ pub struct NetworkService<T: BeaconChainTypes> {
} }
impl<T: BeaconChainTypes> NetworkService<T> { impl<T: BeaconChainTypes> NetworkService<T> {
#[allow(clippy::type_complexity)] async fn build(
pub async fn start(
beacon_chain: Arc<BeaconChain<T>>, beacon_chain: Arc<BeaconChain<T>>,
config: &NetworkConfig, config: &NetworkConfig,
executor: task_executor::TaskExecutor, executor: task_executor::TaskExecutor,
gossipsub_registry: Option<&'_ mut Registry>, gossipsub_registry: Option<&'_ mut Registry>,
beacon_processor_send: BeaconProcessorSend<T::EthSpec>, beacon_processor_send: BeaconProcessorSend<T::EthSpec>,
beacon_processor_reprocess_tx: mpsc::Sender<ReprocessQueueMessage>, beacon_processor_reprocess_tx: mpsc::Sender<ReprocessQueueMessage>,
) -> error::Result<(Arc<NetworkGlobals<T::EthSpec>>, NetworkSenders<T::EthSpec>)> { ) -> error::Result<(
NetworkService<T>,
Arc<NetworkGlobals<T::EthSpec>>,
NetworkSenders<T::EthSpec>,
)> {
let network_log = executor.log().clone(); let network_log = executor.log().clone();
// build the channels for external comms // build the channels for external comms
let (network_senders, network_recievers) = NetworkSenders::new(); let (network_senders, network_recievers) = NetworkSenders::new();
@ -369,6 +372,28 @@ impl<T: BeaconChainTypes> NetworkService<T> {
enable_light_client_server: config.enable_light_client_server, enable_light_client_server: config.enable_light_client_server,
}; };
Ok((network_service, network_globals, network_senders))
}
#[allow(clippy::type_complexity)]
pub async fn start(
beacon_chain: Arc<BeaconChain<T>>,
config: &NetworkConfig,
executor: task_executor::TaskExecutor,
gossipsub_registry: Option<&'_ mut Registry>,
beacon_processor_send: BeaconProcessorSend<T::EthSpec>,
beacon_processor_reprocess_tx: mpsc::Sender<ReprocessQueueMessage>,
) -> error::Result<(Arc<NetworkGlobals<T::EthSpec>>, NetworkSenders<T::EthSpec>)> {
let (network_service, network_globals, network_senders) = Self::build(
beacon_chain,
config,
executor.clone(),
gossipsub_registry,
beacon_processor_send,
beacon_processor_reprocess_tx,
)
.await?;
network_service.spawn_service(executor); network_service.spawn_service(executor);
Ok((network_globals, network_senders)) Ok((network_globals, network_senders))
@ -885,9 +910,10 @@ impl<T: BeaconChainTypes> NetworkService<T> {
fn update_next_fork(&mut self) { fn update_next_fork(&mut self) {
let new_enr_fork_id = self.beacon_chain.enr_fork_id(); let new_enr_fork_id = self.beacon_chain.enr_fork_id();
let new_fork_digest = new_enr_fork_id.fork_digest;
let fork_context = &self.fork_context; let fork_context = &self.fork_context;
if let Some(new_fork_name) = fork_context.from_context_bytes(new_enr_fork_id.fork_digest) { if let Some(new_fork_name) = fork_context.from_context_bytes(new_fork_digest) {
info!( info!(
self.log, self.log,
"Transitioned to new fork"; "Transitioned to new fork";
@ -910,6 +936,10 @@ impl<T: BeaconChainTypes> NetworkService<T> {
Box::pin(next_fork_subscriptions_delay(&self.beacon_chain).into()); Box::pin(next_fork_subscriptions_delay(&self.beacon_chain).into());
self.next_unsubscribe = Box::pin(Some(tokio::time::sleep(unsubscribe_delay)).into()); self.next_unsubscribe = Box::pin(Some(tokio::time::sleep(unsubscribe_delay)).into());
info!(self.log, "Network will unsubscribe from old fork gossip topics in a few epochs"; "remaining_epochs" => UNSUBSCRIBE_DELAY_EPOCHS); info!(self.log, "Network will unsubscribe from old fork gossip topics in a few epochs"; "remaining_epochs" => UNSUBSCRIBE_DELAY_EPOCHS);
// Remove topic weight from old fork topics to prevent peers that left on the mesh on
// old topics from being penalized for not sending us messages.
self.libp2p.remove_topic_weight_except(new_fork_digest);
} else { } else {
crit!(self.log, "Unknown new enr fork id"; "new_fork_id" => ?new_enr_fork_id); crit!(self.log, "Unknown new enr fork id"; "new_fork_id" => ?new_enr_fork_id);
} }

View File

@ -4,14 +4,26 @@ mod tests {
use crate::persisted_dht::load_dht; use crate::persisted_dht::load_dht;
use crate::{NetworkConfig, NetworkService}; use crate::{NetworkConfig, NetworkService};
use beacon_chain::test_utils::BeaconChainHarness; use beacon_chain::test_utils::BeaconChainHarness;
use beacon_processor::BeaconProcessorChannels; use beacon_chain::BeaconChainTypes;
use lighthouse_network::Enr; use beacon_processor::{BeaconProcessorChannels, BeaconProcessorConfig};
use futures::StreamExt;
use lighthouse_network::types::{GossipEncoding, GossipKind};
use lighthouse_network::{Enr, GossipTopic};
use slog::{o, Drain, Level, Logger}; use slog::{o, Drain, Level, Logger};
use sloggers::{null::NullLoggerBuilder, Build}; use sloggers::{null::NullLoggerBuilder, Build};
use std::str::FromStr; use std::str::FromStr;
use std::sync::Arc; use std::sync::Arc;
use tokio::runtime::Runtime; use tokio::runtime::Runtime;
use types::MinimalEthSpec as E; use types::{Epoch, EthSpec, ForkName, MinimalEthSpec, SubnetId};
impl<T: BeaconChainTypes> NetworkService<T> {
fn get_topic_params(
&self,
topic: GossipTopic,
) -> Option<&lighthouse_network::libp2p::gossipsub::TopicScoreParams> {
self.libp2p.get_topic_params(topic)
}
}
fn get_logger(actual_log: bool) -> Logger { fn get_logger(actual_log: bool) -> Logger {
if actual_log { if actual_log {
@ -35,7 +47,7 @@ mod tests {
fn test_dht_persistence() { fn test_dht_persistence() {
let log = get_logger(false); let log = get_logger(false);
let beacon_chain = BeaconChainHarness::builder(E) let beacon_chain = BeaconChainHarness::builder(MinimalEthSpec)
.default_spec() .default_spec()
.deterministic_keypairs(8) .deterministic_keypairs(8)
.fresh_ephemeral_store() .fresh_ephemeral_store()
@ -102,4 +114,126 @@ mod tests {
"should have persisted the second ENR to store" "should have persisted the second ENR to store"
); );
} }
// Test removing topic weight on old topics when a fork happens.
#[test]
fn test_removing_topic_weight_on_old_topics() {
let runtime = Arc::new(Runtime::new().unwrap());
// Capella spec
let mut spec = MinimalEthSpec::default_spec();
spec.altair_fork_epoch = Some(Epoch::new(0));
spec.bellatrix_fork_epoch = Some(Epoch::new(0));
spec.capella_fork_epoch = Some(Epoch::new(1));
// Build beacon chain.
let beacon_chain = BeaconChainHarness::builder(MinimalEthSpec)
.spec(spec.clone())
.deterministic_keypairs(8)
.fresh_ephemeral_store()
.mock_execution_layer()
.build()
.chain;
let (next_fork_name, _) = beacon_chain.duration_to_next_fork().expect("next fork");
assert_eq!(next_fork_name, ForkName::Capella);
// Build network service.
let (mut network_service, network_globals, _network_senders) = runtime.block_on(async {
let (_, exit) = exit_future::signal();
let (shutdown_tx, _) = futures::channel::mpsc::channel(1);
let executor = task_executor::TaskExecutor::new(
Arc::downgrade(&runtime),
exit,
get_logger(false),
shutdown_tx,
);
let mut config = NetworkConfig::default();
config.set_ipv4_listening_address(std::net::Ipv4Addr::UNSPECIFIED, 21214, 21214, 21215);
config.discv5_config.table_filter = |_| true; // Do not ignore local IPs
config.upnp_enabled = false;
let beacon_processor_channels =
BeaconProcessorChannels::new(&BeaconProcessorConfig::default());
NetworkService::build(
beacon_chain.clone(),
&config,
executor.clone(),
None,
beacon_processor_channels.beacon_processor_tx,
beacon_processor_channels.work_reprocessing_tx,
)
.await
.unwrap()
});
// Subscribe to the topics.
runtime.block_on(async {
while network_globals.gossipsub_subscriptions.read().len() < 2 {
if let Some(msg) = network_service.attestation_service.next().await {
network_service.on_attestation_service_msg(msg);
}
}
});
// Make sure the service is subscribed to the topics.
let (old_topic1, old_topic2) = {
let mut subnets = SubnetId::compute_subnets_for_epoch::<MinimalEthSpec>(
network_globals.local_enr().node_id().raw().into(),
beacon_chain.epoch().unwrap(),
&spec,
)
.unwrap()
.0
.collect::<Vec<_>>();
assert_eq!(2, subnets.len());
let old_fork_digest = beacon_chain.enr_fork_id().fork_digest;
let old_topic1 = GossipTopic::new(
GossipKind::Attestation(subnets.pop().unwrap()),
GossipEncoding::SSZSnappy,
old_fork_digest,
);
let old_topic2 = GossipTopic::new(
GossipKind::Attestation(subnets.pop().unwrap()),
GossipEncoding::SSZSnappy,
old_fork_digest,
);
(old_topic1, old_topic2)
};
let subscriptions = network_globals.gossipsub_subscriptions.read().clone();
assert_eq!(2, subscriptions.len());
assert!(subscriptions.contains(&old_topic1));
assert!(subscriptions.contains(&old_topic2));
let old_topic_params1 = network_service
.get_topic_params(old_topic1.clone())
.expect("topic score params");
assert!(old_topic_params1.topic_weight > 0.0);
let old_topic_params2 = network_service
.get_topic_params(old_topic2.clone())
.expect("topic score params");
assert!(old_topic_params2.topic_weight > 0.0);
// Advance slot to the next fork
for _ in 0..MinimalEthSpec::slots_per_epoch() {
beacon_chain.slot_clock.advance_slot();
}
// Run `NetworkService::update_next_fork()`.
runtime.block_on(async {
network_service.update_next_fork();
});
// Check that topic_weight on the old topics has been zeroed.
let old_topic_params1 = network_service
.get_topic_params(old_topic1)
.expect("topic score params");
assert_eq!(0.0, old_topic_params1.topic_weight);
let old_topic_params2 = network_service
.get_topic_params(old_topic2)
.expect("topic score params");
assert_eq!(0.0, old_topic_params2.topic_weight);
}
} }

View File

@ -22,6 +22,7 @@ use std::fmt::Debug;
use std::fs; use std::fs;
use std::net::Ipv6Addr; use std::net::Ipv6Addr;
use std::net::{IpAddr, Ipv4Addr, ToSocketAddrs}; use std::net::{IpAddr, Ipv4Addr, ToSocketAddrs};
use std::num::NonZeroU16;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::str::FromStr; use std::str::FromStr;
use std::time::Duration; use std::time::Duration;
@ -1216,23 +1217,23 @@ pub fn set_network_config(
if let Some(enr_udp_port_str) = cli_args.value_of("enr-udp-port") { if let Some(enr_udp_port_str) = cli_args.value_of("enr-udp-port") {
config.enr_udp4_port = Some( config.enr_udp4_port = Some(
enr_udp_port_str enr_udp_port_str
.parse::<u16>() .parse::<NonZeroU16>()
.map_err(|_| format!("Invalid discovery port: {}", enr_udp_port_str))?, .map_err(|_| format!("Invalid ENR discovery port: {}", enr_udp_port_str))?,
); );
} }
if let Some(enr_quic_port_str) = cli_args.value_of("enr-quic-port") { if let Some(enr_quic_port_str) = cli_args.value_of("enr-quic-port") {
config.enr_quic4_port = Some( config.enr_quic4_port = Some(
enr_quic_port_str enr_quic_port_str
.parse::<u16>() .parse::<NonZeroU16>()
.map_err(|_| format!("Invalid quic port: {}", enr_quic_port_str))?, .map_err(|_| format!("Invalid ENR quic port: {}", enr_quic_port_str))?,
); );
} }
if let Some(enr_tcp_port_str) = cli_args.value_of("enr-tcp-port") { if let Some(enr_tcp_port_str) = cli_args.value_of("enr-tcp-port") {
config.enr_tcp4_port = Some( config.enr_tcp4_port = Some(
enr_tcp_port_str enr_tcp_port_str
.parse::<u16>() .parse::<NonZeroU16>()
.map_err(|_| format!("Invalid ENR TCP port: {}", enr_tcp_port_str))?, .map_err(|_| format!("Invalid ENR TCP port: {}", enr_tcp_port_str))?,
); );
} }
@ -1240,23 +1241,23 @@ pub fn set_network_config(
if let Some(enr_udp_port_str) = cli_args.value_of("enr-udp6-port") { if let Some(enr_udp_port_str) = cli_args.value_of("enr-udp6-port") {
config.enr_udp6_port = Some( config.enr_udp6_port = Some(
enr_udp_port_str enr_udp_port_str
.parse::<u16>() .parse::<NonZeroU16>()
.map_err(|_| format!("Invalid discovery port: {}", enr_udp_port_str))?, .map_err(|_| format!("Invalid ENR discovery port: {}", enr_udp_port_str))?,
); );
} }
if let Some(enr_quic_port_str) = cli_args.value_of("enr-quic6-port") { if let Some(enr_quic_port_str) = cli_args.value_of("enr-quic6-port") {
config.enr_quic6_port = Some( config.enr_quic6_port = Some(
enr_quic_port_str enr_quic_port_str
.parse::<u16>() .parse::<NonZeroU16>()
.map_err(|_| format!("Invalid quic port: {}", enr_quic_port_str))?, .map_err(|_| format!("Invalid ENR quic port: {}", enr_quic_port_str))?,
); );
} }
if let Some(enr_tcp_port_str) = cli_args.value_of("enr-tcp6-port") { if let Some(enr_tcp_port_str) = cli_args.value_of("enr-tcp6-port") {
config.enr_tcp6_port = Some( config.enr_tcp6_port = Some(
enr_tcp_port_str enr_tcp_port_str
.parse::<u16>() .parse::<NonZeroU16>()
.map_err(|_| format!("Invalid ENR TCP port: {}", enr_tcp_port_str))?, .map_err(|_| format!("Invalid ENR TCP port: {}", enr_tcp_port_str))?,
); );
} }
@ -1264,25 +1265,38 @@ pub fn set_network_config(
if cli_args.is_present("enr-match") { if cli_args.is_present("enr-match") {
// Match the IP and UDP port in the ENR. // Match the IP and UDP port in the ENR.
// Set the ENR address to localhost if the address is unspecified.
if let Some(ipv4_addr) = config.listen_addrs().v4().cloned() { if let Some(ipv4_addr) = config.listen_addrs().v4().cloned() {
// ensure the port is valid to be advertised
let disc_port = ipv4_addr
.disc_port
.try_into()
.map_err(|_| "enr-match can only be used with non-zero listening ports")?;
// Set the ENR address to localhost if the address is unspecified.
let ipv4_enr_addr = if ipv4_addr.addr == Ipv4Addr::UNSPECIFIED { let ipv4_enr_addr = if ipv4_addr.addr == Ipv4Addr::UNSPECIFIED {
Ipv4Addr::LOCALHOST Ipv4Addr::LOCALHOST
} else { } else {
ipv4_addr.addr ipv4_addr.addr
}; };
config.enr_address.0 = Some(ipv4_enr_addr); config.enr_address.0 = Some(ipv4_enr_addr);
config.enr_udp4_port = Some(ipv4_addr.disc_port); config.enr_udp4_port = Some(disc_port);
} }
if let Some(ipv6_addr) = config.listen_addrs().v6().cloned() { if let Some(ipv6_addr) = config.listen_addrs().v6().cloned() {
// ensure the port is valid to be advertised
let disc_port = ipv6_addr
.disc_port
.try_into()
.map_err(|_| "enr-match can only be used with non-zero listening ports")?;
// Set the ENR address to localhost if the address is unspecified.
let ipv6_enr_addr = if ipv6_addr.addr == Ipv6Addr::UNSPECIFIED { let ipv6_enr_addr = if ipv6_addr.addr == Ipv6Addr::UNSPECIFIED {
Ipv6Addr::LOCALHOST Ipv6Addr::LOCALHOST
} else { } else {
ipv6_addr.addr ipv6_addr.addr
}; };
config.enr_address.1 = Some(ipv6_enr_addr); config.enr_address.1 = Some(ipv6_enr_addr);
config.enr_udp6_port = Some(ipv6_addr.disc_port); config.enr_udp6_port = Some(disc_port);
} }
} }

View File

@ -60,19 +60,25 @@ impl<T: EthSpec> BootNodeConfig<T> {
// Set the Enr Discovery ports to the listening ports if not present. // Set the Enr Discovery ports to the listening ports if not present.
if let Some(listening_addr_v4) = network_config.listen_addrs().v4() { if let Some(listening_addr_v4) = network_config.listen_addrs().v4() {
network_config.enr_udp4_port = Some( if network_config.enr_udp4_port.is_none() {
network_config network_config.enr_udp4_port =
.enr_udp4_port Some(network_config.enr_udp4_port.unwrap_or(
.unwrap_or(listening_addr_v4.disc_port), listening_addr_v4.disc_port.try_into().map_err(|_| {
) "boot node enr-udp-port not set and listening port is zero"
})?,
))
}
}; };
if let Some(listening_addr_v6) = network_config.listen_addrs().v6() { if let Some(listening_addr_v6) = network_config.listen_addrs().v6() {
network_config.enr_udp6_port = Some( if network_config.enr_udp6_port.is_none() {
network_config network_config.enr_udp6_port =
.enr_udp6_port Some(network_config.enr_udp6_port.unwrap_or(
.unwrap_or(listening_addr_v6.disc_port), listening_addr_v6.disc_port.try_into().map_err(|_| {
) "boot node enr-udp-port not set and listening port is zero"
})?,
))
}
}; };
// By default this is enabled. If it is not set, revert to false. // By default this is enabled. If it is not set, revert to false.

View File

@ -1,5 +1,4 @@
status = [ status = [
"cargo-fmt",
"release-tests-ubuntu", "release-tests-ubuntu",
"release-tests-windows", "release-tests-windows",
"debug-tests-ubuntu", "debug-tests-ubuntu",
@ -9,20 +8,16 @@ status = [
"eth1-simulator-ubuntu", "eth1-simulator-ubuntu",
"merge-transition-ubuntu", "merge-transition-ubuntu",
"no-eth1-simulator-ubuntu", "no-eth1-simulator-ubuntu",
"check-benchmarks", "check-code",
"clippy",
"arbitrary-check",
"cargo-audit",
"cargo-udeps", "cargo-udeps",
"beacon-chain-tests", "beacon-chain-tests",
"op-pool-tests", "op-pool-tests",
"doppelganger-protection-test", "doppelganger-protection-test",
"execution-engine-integration-ubuntu", "execution-engine-integration-ubuntu",
"cargo-vendor",
"check-msrv", "check-msrv",
"slasher-tests", "slasher-tests",
"syncing-simulator-ubuntu", "syncing-simulator-ubuntu",
"compile-with-beta-compiler" "compile-with-beta-compiler",
] ]
use_squash_merge = true use_squash_merge = true
timeout_sec = 10800 timeout_sec = 10800

View File

@ -9,8 +9,6 @@
- enr:-Ku4QPn5eVhcoF1opaFEvg1b6JNFD2rqVkHQ8HApOKK61OIcIXD127bKWgAtbwI7pnxx6cDyk_nI88TrZKQaGMZj0q0Bh2F0dG5ldHOIAAAAAAAAAACEZXRoMpC1MD8qAAAAAP__________gmlkgnY0gmlwhDayLMaJc2VjcDI1NmsxoQK2sBOLGcUb4AwuYzFuAVCaNHA-dy24UuEKkeFNgCVCsIN1ZHCCIyg - enr:-Ku4QPn5eVhcoF1opaFEvg1b6JNFD2rqVkHQ8HApOKK61OIcIXD127bKWgAtbwI7pnxx6cDyk_nI88TrZKQaGMZj0q0Bh2F0dG5ldHOIAAAAAAAAAACEZXRoMpC1MD8qAAAAAP__________gmlkgnY0gmlwhDayLMaJc2VjcDI1NmsxoQK2sBOLGcUb4AwuYzFuAVCaNHA-dy24UuEKkeFNgCVCsIN1ZHCCIyg
- enr:-Ku4QEWzdnVtXc2Q0ZVigfCGggOVB2Vc1ZCPEc6j21NIFLODSJbvNaef1g4PxhPwl_3kax86YPheFUSLXPRs98vvYsoBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpC1MD8qAAAAAP__________gmlkgnY0gmlwhDZBrP2Jc2VjcDI1NmsxoQM6jr8Rb1ktLEsVcKAPa08wCsKUmvoQ8khiOl_SLozf9IN1ZHCCIyg - enr:-Ku4QEWzdnVtXc2Q0ZVigfCGggOVB2Vc1ZCPEc6j21NIFLODSJbvNaef1g4PxhPwl_3kax86YPheFUSLXPRs98vvYsoBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpC1MD8qAAAAAP__________gmlkgnY0gmlwhDZBrP2Jc2VjcDI1NmsxoQM6jr8Rb1ktLEsVcKAPa08wCsKUmvoQ8khiOl_SLozf9IN1ZHCCIyg
# Teku team (Consensys) # Teku team (Consensys)
- enr:-KG4QOtcP9X1FbIMOe17QNMKqDxCpm14jcX5tiOE4_TyMrFqbmhPZHK_ZPG2Gxb1GE2xdtodOfx9-cgvNtxnRyHEmC0ghGV0aDKQ9aX9QgAAAAD__________4JpZIJ2NIJpcIQDE8KdiXNlY3AyNTZrMaEDhpehBDbZjM_L9ek699Y7vhUJ-eAdMyQW_Fil522Y0fODdGNwgiMog3VkcIIjKA
- enr:-KG4QL-eqFoHy0cI31THvtZjpYUu_Jdw_MO7skQRJxY1g5HTN1A0epPCU6vi0gLGUgrzpU-ygeMSS8ewVxDpKfYmxMMGhGV0aDKQtTA_KgAAAAD__________4JpZIJ2NIJpcIQ2_DUbiXNlY3AyNTZrMaED8GJ2vzUqgL6-KD1xalo1CsmY4X1HaDnyl6Y_WayCo9GDdGNwgiMog3VkcIIjKA
- enr:-KG4QMOEswP62yzDjSwWS4YEjtTZ5PO6r65CPqYBkgTTkrpaedQ8uEUo1uMALtJIvb2w_WWEVmg5yt1UAuK1ftxUU7QDhGV0aDKQu6TalgMAAAD__________4JpZIJ2NIJpcIQEnfA2iXNlY3AyNTZrMaEDfol8oLr6XJ7FsdAYE7lpJhKMls4G_v6qQOGKJUWGb_uDdGNwgiMog3VkcIIjKA - enr:-KG4QMOEswP62yzDjSwWS4YEjtTZ5PO6r65CPqYBkgTTkrpaedQ8uEUo1uMALtJIvb2w_WWEVmg5yt1UAuK1ftxUU7QDhGV0aDKQu6TalgMAAAD__________4JpZIJ2NIJpcIQEnfA2iXNlY3AyNTZrMaEDfol8oLr6XJ7FsdAYE7lpJhKMls4G_v6qQOGKJUWGb_uDdGNwgiMog3VkcIIjKA
- enr:-KG4QF4B5WrlFcRhUU6dZETwY5ZzAXnA0vGC__L1Kdw602nDZwXSTs5RFXFIFUnbQJmhNGVU6OIX7KVrCSTODsz1tK4DhGV0aDKQu6TalgMAAAD__________4JpZIJ2NIJpcIQExNYEiXNlY3AyNTZrMaECQmM9vp7KhaXhI-nqL_R0ovULLCFSFTa9CPPSdb1zPX6DdGNwgiMog3VkcIIjKA - enr:-KG4QF4B5WrlFcRhUU6dZETwY5ZzAXnA0vGC__L1Kdw602nDZwXSTs5RFXFIFUnbQJmhNGVU6OIX7KVrCSTODsz1tK4DhGV0aDKQu6TalgMAAAD__________4JpZIJ2NIJpcIQExNYEiXNlY3AyNTZrMaECQmM9vp7KhaXhI-nqL_R0ovULLCFSFTa9CPPSdb1zPX6DdGNwgiMog3VkcIIjKA
# Prysm team (Prysmatic Labs) # Prysm team (Prysmatic Labs)

View File

@ -6,3 +6,6 @@ edition = { workspace = true }
[dependencies] [dependencies]
fnv = { workspace = true } fnv = { workspace = true }
[dev-dependencies]
mock_instant = "0.3"

View File

@ -1,7 +1,13 @@
//! This implements a time-based LRU cache for fast checking of duplicates //! This implements a time-based LRU cache for fast checking of duplicates
use fnv::FnvHashSet; use fnv::FnvHashSet;
#[cfg(test)]
use mock_instant::Instant;
use std::collections::VecDeque; use std::collections::VecDeque;
use std::time::{Duration, Instant};
#[cfg(not(test))]
use std::time::Instant;
use std::time::Duration;
struct Element<Key> { struct Element<Key> {
/// The key being inserted. /// The key being inserted.
@ -222,16 +228,16 @@ mod test {
cache.insert("a"); cache.insert("a");
cache.insert("b"); cache.insert("b");
std::thread::sleep(Duration::from_millis(20)); mock_instant::MockClock::advance(Duration::from_millis(20));
cache.insert("a"); cache.insert("a");
// a is newer now // a is newer now
std::thread::sleep(Duration::from_millis(85)); mock_instant::MockClock::advance(Duration::from_millis(85));
assert!(cache.contains(&"a"),); assert!(cache.contains(&"a"),);
// b was inserted first but was not as recent it should have been removed // b was inserted first but was not as recent it should have been removed
assert!(!cache.contains(&"b")); assert!(!cache.contains(&"b"));
std::thread::sleep(Duration::from_millis(16)); mock_instant::MockClock::advance(Duration::from_millis(16));
assert!(!cache.contains(&"a")); assert!(!cache.contains(&"a"));
} }
} }

View File

@ -1,4 +1,4 @@
#![cfg(all(test, not(feature = "fake_crypto")))] #![cfg(all(test, not(feature = "fake_crypto"), not(debug_assertions)))]
use crate::per_block_processing::errors::{ use crate::per_block_processing::errors::{
AttestationInvalid, AttesterSlashingInvalid, BlockOperationError, BlockProcessingError, AttestationInvalid, AttesterSlashingInvalid, BlockOperationError, BlockProcessingError,

View File

@ -1,8 +1,8 @@
use crate::beacon_block_body::KzgCommitments; use crate::beacon_block_body::KzgCommitments;
use crate::{ use crate::{
BlobRootsList, ChainSpec, EthSpec, ExecutionPayloadHeaderCapella, ExecutionPayloadHeaderDeneb, BlobRootsList, ChainSpec, EthSpec, ExecutionPayloadHeaderCapella, ExecutionPayloadHeaderDeneb,
ExecutionPayloadHeaderMerge, ExecutionPayloadHeaderRef, ForkName, ForkVersionDeserialize, ExecutionPayloadHeaderMerge, ExecutionPayloadHeaderRef, ExecutionPayloadHeaderRefMut, ForkName,
KzgProofs, SignedRoot, Uint256, ForkVersionDeserialize, KzgProofs, SignedRoot, Uint256,
}; };
use bls::PublicKeyBytes; use bls::PublicKeyBytes;
use bls::Signature; use bls::Signature;
@ -26,7 +26,8 @@ pub struct BlindedBlobsBundle<E: EthSpec> {
derive(PartialEq, Debug, Serialize, Deserialize, TreeHash, Clone), derive(PartialEq, Debug, Serialize, Deserialize, TreeHash, Clone),
serde(bound = "E: EthSpec", deny_unknown_fields) serde(bound = "E: EthSpec", deny_unknown_fields)
), ),
map_ref_into(ExecutionPayloadHeaderRef) map_ref_into(ExecutionPayloadHeaderRef),
map_ref_mut_into(ExecutionPayloadHeaderRefMut)
)] )]
#[derive(PartialEq, Debug, Serialize, Deserialize, TreeHash, Clone)] #[derive(PartialEq, Debug, Serialize, Deserialize, TreeHash, Clone)]
#[serde(bound = "E: EthSpec", deny_unknown_fields, untagged)] #[serde(bound = "E: EthSpec", deny_unknown_fields, untagged)]
@ -59,6 +60,14 @@ impl<'a, E: EthSpec> BuilderBidRef<'a, E> {
} }
} }
impl<'a, E: EthSpec> BuilderBidRefMut<'a, E> {
pub fn header_mut(self) -> ExecutionPayloadHeaderRefMut<'a, E> {
map_builder_bid_ref_mut_into_execution_payload_header_ref_mut!(&'a _, self, |bid, cons| {
cons(&mut bid.header)
})
}
}
impl<E: EthSpec> SignedRoot for BuilderBid<E> {} impl<E: EthSpec> SignedRoot for BuilderBid<E> {}
/// Validator registration, for use in interacting with servers implementing the builder API. /// Validator registration, for use in interacting with servers implementing the builder API.

View File

@ -21,3 +21,17 @@ pub struct ValidatorRegistrationData {
} }
impl SignedRoot for ValidatorRegistrationData {} impl SignedRoot for ValidatorRegistrationData {}
impl SignedValidatorRegistrationData {
pub fn verify_signature(&self, spec: &ChainSpec) -> bool {
self.message
.pubkey
.decompress()
.map(|pubkey| {
let domain = spec.get_builder_domain();
let message = self.message.signing_root(domain);
self.signature.verify(&pubkey, message)
})
.unwrap_or(false)
}
}

View File

@ -4,16 +4,16 @@ use lighthouse_network::{
libp2p::identity::secp256k1, libp2p::identity::secp256k1,
NetworkConfig, NETWORK_KEY_FILENAME, NetworkConfig, NETWORK_KEY_FILENAME,
}; };
use std::fs::File;
use std::io::Write; use std::io::Write;
use std::path::PathBuf; use std::path::PathBuf;
use std::{fs, net::Ipv4Addr}; use std::{fs, net::Ipv4Addr};
use std::{fs::File, num::NonZeroU16};
use types::{ChainSpec, EnrForkId, Epoch, EthSpec, Hash256}; use types::{ChainSpec, EnrForkId, Epoch, EthSpec, Hash256};
pub fn run<T: EthSpec>(matches: &ArgMatches) -> Result<(), String> { pub fn run<T: EthSpec>(matches: &ArgMatches) -> Result<(), String> {
let ip: Ipv4Addr = clap_utils::parse_required(matches, "ip")?; let ip: Ipv4Addr = clap_utils::parse_required(matches, "ip")?;
let udp_port: u16 = clap_utils::parse_required(matches, "udp-port")?; let udp_port: NonZeroU16 = clap_utils::parse_required(matches, "udp-port")?;
let tcp_port: u16 = clap_utils::parse_required(matches, "tcp-port")?; let tcp_port: NonZeroU16 = clap_utils::parse_required(matches, "tcp-port")?;
let output_dir: PathBuf = clap_utils::parse_required(matches, "output-dir")?; let output_dir: PathBuf = clap_utils::parse_required(matches, "output-dir")?;
let genesis_fork_version: [u8; 4] = let genesis_fork_version: [u8; 4] =
clap_utils::parse_ssz_required(matches, "genesis-fork-version")?; clap_utils::parse_ssz_required(matches, "genesis-fork-version")?;

View File

@ -22,9 +22,14 @@ use types::{
Address, Checkpoint, Epoch, ExecutionBlockHash, ForkName, Hash256, MainnetEthSpec, Address, Checkpoint, Epoch, ExecutionBlockHash, ForkName, Hash256, MainnetEthSpec,
ProgressiveBalancesMode, ProgressiveBalancesMode,
}; };
use unused_port::{unused_tcp4_port, unused_tcp6_port, unused_udp4_port, unused_udp6_port};
const DEFAULT_ETH1_ENDPOINT: &str = "http://localhost:8545/"; const DEFAULT_ETH1_ENDPOINT: &str = "http://localhost:8545/";
const DUMMY_ENR_TCP_PORT: u16 = 7777;
const DUMMY_ENR_UDP_PORT: u16 = 8888;
const DUMMY_ENR_QUIC_PORT: u16 = 9999;
const _: () =
assert!(DUMMY_ENR_QUIC_PORT != 0 && DUMMY_ENR_TCP_PORT != 0 && DUMMY_ENR_UDP_PORT != 0);
/// Returns the `lighthouse beacon_node` command. /// Returns the `lighthouse beacon_node` command.
fn base_cmd() -> Command { fn base_cmd() -> Command {
@ -1036,7 +1041,7 @@ fn network_listen_address_flag_wrong_double_v6_value_config() {
} }
#[test] #[test]
fn network_port_flag_over_ipv4() { fn network_port_flag_over_ipv4() {
let port = unused_tcp4_port().expect("Unable to find unused port."); let port = 0;
CommandLineTest::new() CommandLineTest::new()
.flag("port", Some(port.to_string().as_str())) .flag("port", Some(port.to_string().as_str()))
.run() .run()
@ -1053,7 +1058,7 @@ fn network_port_flag_over_ipv4() {
} }
#[test] #[test]
fn network_port_flag_over_ipv6() { fn network_port_flag_over_ipv6() {
let port = unused_tcp6_port().expect("Unable to find unused port."); let port = 0;
CommandLineTest::new() CommandLineTest::new()
.flag("listen-address", Some("::1")) .flag("listen-address", Some("::1"))
.flag("port", Some(port.to_string().as_str())) .flag("port", Some(port.to_string().as_str()))
@ -1071,8 +1076,8 @@ fn network_port_flag_over_ipv6() {
} }
#[test] #[test]
fn network_port_and_discovery_port_flags_over_ipv4() { fn network_port_and_discovery_port_flags_over_ipv4() {
let tcp4_port = unused_tcp4_port().expect("Unable to find unused port."); let tcp4_port = 0;
let disc4_port = unused_udp4_port().expect("Unable to find unused port."); let disc4_port = 0;
CommandLineTest::new() CommandLineTest::new()
.flag("port", Some(tcp4_port.to_string().as_str())) .flag("port", Some(tcp4_port.to_string().as_str()))
.flag("discovery-port", Some(disc4_port.to_string().as_str())) .flag("discovery-port", Some(disc4_port.to_string().as_str()))
@ -1090,8 +1095,8 @@ fn network_port_and_discovery_port_flags_over_ipv4() {
} }
#[test] #[test]
fn network_port_and_discovery_port_flags_over_ipv6() { fn network_port_and_discovery_port_flags_over_ipv6() {
let tcp6_port = unused_tcp6_port().expect("Unable to find unused port."); let tcp6_port = 0;
let disc6_port = unused_udp6_port().expect("Unable to find unused port."); let disc6_port = 0;
CommandLineTest::new() CommandLineTest::new()
.flag("listen-address", Some("::1")) .flag("listen-address", Some("::1"))
.flag("port", Some(tcp6_port.to_string().as_str())) .flag("port", Some(tcp6_port.to_string().as_str()))
@ -1110,10 +1115,10 @@ fn network_port_and_discovery_port_flags_over_ipv6() {
} }
#[test] #[test]
fn network_port_and_discovery_port_flags_over_ipv4_and_ipv6() { fn network_port_and_discovery_port_flags_over_ipv4_and_ipv6() {
let tcp4_port = unused_tcp4_port().expect("Unable to find unused port."); let tcp4_port = 0;
let disc4_port = unused_udp4_port().expect("Unable to find unused port."); let disc4_port = 0;
let tcp6_port = unused_tcp6_port().expect("Unable to find unused port."); let tcp6_port = 0;
let disc6_port = unused_udp6_port().expect("Unable to find unused port."); let disc6_port = 0;
CommandLineTest::new() CommandLineTest::new()
.flag("listen-address", Some("::1")) .flag("listen-address", Some("::1"))
.flag("listen-address", Some("127.0.0.1")) .flag("listen-address", Some("127.0.0.1"))
@ -1145,12 +1150,12 @@ fn network_port_and_discovery_port_flags_over_ipv4_and_ipv6() {
#[test] #[test]
fn network_port_discovery_quic_port_flags_over_ipv4_and_ipv6() { fn network_port_discovery_quic_port_flags_over_ipv4_and_ipv6() {
let tcp4_port = unused_tcp4_port().expect("Unable to find unused port."); let tcp4_port = 0;
let disc4_port = unused_udp4_port().expect("Unable to find unused port."); let disc4_port = 0;
let quic4_port = unused_udp4_port().expect("Unable to find unused port."); let quic4_port = 0;
let tcp6_port = unused_tcp6_port().expect("Unable to find unused port."); let tcp6_port = 0;
let disc6_port = unused_udp6_port().expect("Unable to find unused port."); let disc6_port = 0;
let quic6_port = unused_udp6_port().expect("Unable to find unused port."); let quic6_port = 0;
CommandLineTest::new() CommandLineTest::new()
.flag("listen-address", Some("::1")) .flag("listen-address", Some("::1"))
.flag("listen-address", Some("127.0.0.1")) .flag("listen-address", Some("127.0.0.1"))
@ -1226,7 +1231,7 @@ fn default_backfill_rate_limiting_flag() {
} }
#[test] #[test]
fn default_boot_nodes() { fn default_boot_nodes() {
let number_of_boot_nodes = 17; let number_of_boot_nodes = 15;
CommandLineTest::new() CommandLineTest::new()
.run_with_zero_port() .run_with_zero_port()
@ -1296,57 +1301,91 @@ fn network_load_flag() {
// Tests for ENR flags. // Tests for ENR flags.
#[test] #[test]
fn enr_udp_port_flag() { fn enr_udp_port_flag() {
let port = unused_udp4_port().expect("Unable to find unused port."); let port = DUMMY_ENR_UDP_PORT;
assert!(port != 0);
CommandLineTest::new() CommandLineTest::new()
.flag("enr-udp-port", Some(port.to_string().as_str())) .flag("enr-udp-port", Some(port.to_string().as_str()))
.run_with_zero_port() .run_with_zero_port()
.with_config(|config| assert_eq!(config.network.enr_udp4_port, Some(port))); .with_config(|config| {
assert_eq!(
config.network.enr_udp4_port.map(|port| port.get()),
Some(port)
)
});
} }
#[test] #[test]
fn enr_quic_port_flag() { fn enr_quic_port_flag() {
let port = unused_udp4_port().expect("Unable to find unused port."); let port = DUMMY_ENR_QUIC_PORT;
CommandLineTest::new() CommandLineTest::new()
.flag("enr-quic-port", Some(port.to_string().as_str())) .flag("enr-quic-port", Some(port.to_string().as_str()))
.run_with_zero_port() .run_with_zero_port()
.with_config(|config| assert_eq!(config.network.enr_quic4_port, Some(port))); .with_config(|config| {
assert_eq!(
config.network.enr_quic4_port.map(|port| port.get()),
Some(port)
)
});
} }
#[test] #[test]
fn enr_tcp_port_flag() { fn enr_tcp_port_flag() {
let port = unused_tcp4_port().expect("Unable to find unused port."); let port = DUMMY_ENR_TCP_PORT;
CommandLineTest::new() CommandLineTest::new()
.flag("enr-tcp-port", Some(port.to_string().as_str())) .flag("enr-tcp-port", Some(port.to_string().as_str()))
.run_with_zero_port() .run_with_zero_port()
.with_config(|config| assert_eq!(config.network.enr_tcp4_port, Some(port))); .with_config(|config| {
assert_eq!(
config.network.enr_tcp4_port.map(|port| port.get()),
Some(port)
)
});
} }
#[test] #[test]
fn enr_udp6_port_flag() { fn enr_udp6_port_flag() {
let port = unused_udp6_port().expect("Unable to find unused port."); let port = DUMMY_ENR_UDP_PORT;
CommandLineTest::new() CommandLineTest::new()
.flag("enr-udp6-port", Some(port.to_string().as_str())) .flag("enr-udp6-port", Some(port.to_string().as_str()))
.run_with_zero_port() .run_with_zero_port()
.with_config(|config| assert_eq!(config.network.enr_udp6_port, Some(port))); .with_config(|config| {
assert_eq!(
config.network.enr_udp6_port.map(|port| port.get()),
Some(port)
)
});
} }
#[test] #[test]
fn enr_quic6_port_flag() { fn enr_quic6_port_flag() {
let port = unused_udp6_port().expect("Unable to find unused port."); let port = DUMMY_ENR_QUIC_PORT;
CommandLineTest::new() CommandLineTest::new()
.flag("enr-quic6-port", Some(port.to_string().as_str())) .flag("enr-quic6-port", Some(port.to_string().as_str()))
.run_with_zero_port() .run_with_zero_port()
.with_config(|config| assert_eq!(config.network.enr_quic6_port, Some(port))); .with_config(|config| {
assert_eq!(
config.network.enr_quic6_port.map(|port| port.get()),
Some(port)
)
});
} }
#[test] #[test]
fn enr_tcp6_port_flag() { fn enr_tcp6_port_flag() {
let port = unused_tcp6_port().expect("Unable to find unused port."); let port = DUMMY_ENR_TCP_PORT;
CommandLineTest::new() CommandLineTest::new()
.flag("enr-tcp6-port", Some(port.to_string().as_str())) .flag("enr-tcp6-port", Some(port.to_string().as_str()))
.run_with_zero_port() .run_with_zero_port()
.with_config(|config| assert_eq!(config.network.enr_tcp6_port, Some(port))); .with_config(|config| {
assert_eq!(
config.network.enr_tcp6_port.map(|port| port.get()),
Some(port)
)
});
} }
#[test] #[test]
fn enr_match_flag_over_ipv4() { fn enr_match_flag_over_ipv4() {
let addr = "127.0.0.2".parse::<Ipv4Addr>().unwrap(); let addr = "127.0.0.2".parse::<Ipv4Addr>().unwrap();
let udp4_port = unused_udp4_port().expect("Unable to find unused port.");
let tcp4_port = unused_tcp4_port().expect("Unable to find unused port."); // the reason we use the ENR dummy values is because, due to the nature of the `--enr-match` flag, these will eventually become ENR ports (as well as listening ports).
let udp4_port = DUMMY_ENR_UDP_PORT;
let tcp4_port = DUMMY_ENR_TCP_PORT;
CommandLineTest::new() CommandLineTest::new()
.flag("enr-match", None) .flag("enr-match", None)
.flag("listen-address", Some("127.0.0.2")) .flag("listen-address", Some("127.0.0.2"))
@ -1363,15 +1402,21 @@ fn enr_match_flag_over_ipv4() {
Some((addr, udp4_port, tcp4_port)) Some((addr, udp4_port, tcp4_port))
); );
assert_eq!(config.network.enr_address, (Some(addr), None)); assert_eq!(config.network.enr_address, (Some(addr), None));
assert_eq!(config.network.enr_udp4_port, Some(udp4_port)); assert_eq!(
config.network.enr_udp4_port.map(|port| port.get()),
Some(udp4_port)
);
}); });
} }
#[test] #[test]
fn enr_match_flag_over_ipv6() { fn enr_match_flag_over_ipv6() {
const ADDR: &str = "::1"; const ADDR: &str = "::1";
let addr = ADDR.parse::<Ipv6Addr>().unwrap(); let addr = ADDR.parse::<Ipv6Addr>().unwrap();
let udp6_port = unused_udp6_port().expect("Unable to find unused port.");
let tcp6_port = unused_tcp6_port().expect("Unable to find unused port."); // the reason we use the ENR dummy values is because, due to the nature of the `--enr-match` flag, these will eventually become ENR ports (as well as listening ports).
let udp6_port = DUMMY_ENR_UDP_PORT;
let tcp6_port = DUMMY_ENR_TCP_PORT;
CommandLineTest::new() CommandLineTest::new()
.flag("enr-match", None) .flag("enr-match", None)
.flag("listen-address", Some(ADDR)) .flag("listen-address", Some(ADDR))
@ -1388,19 +1433,27 @@ fn enr_match_flag_over_ipv6() {
Some((addr, udp6_port, tcp6_port)) Some((addr, udp6_port, tcp6_port))
); );
assert_eq!(config.network.enr_address, (None, Some(addr))); assert_eq!(config.network.enr_address, (None, Some(addr)));
assert_eq!(config.network.enr_udp6_port, Some(udp6_port)); assert_eq!(
config.network.enr_udp6_port.map(|port| port.get()),
Some(udp6_port)
);
}); });
} }
#[test] #[test]
fn enr_match_flag_over_ipv4_and_ipv6() { fn enr_match_flag_over_ipv4_and_ipv6() {
const IPV6_ADDR: &str = "::1"; const IPV6_ADDR: &str = "::1";
// the reason we use the ENR dummy values is because, due to the nature of the `--enr-match` flag, these will eventually become ENR ports (as well as listening ports).
let udp6_port = DUMMY_ENR_UDP_PORT;
let tcp6_port = DUMMY_ENR_TCP_PORT;
let ipv6_addr = IPV6_ADDR.parse::<Ipv6Addr>().unwrap(); let ipv6_addr = IPV6_ADDR.parse::<Ipv6Addr>().unwrap();
let udp6_port = unused_udp6_port().expect("Unable to find unused port.");
let tcp6_port = unused_tcp6_port().expect("Unable to find unused port.");
const IPV4_ADDR: &str = "127.0.0.1"; const IPV4_ADDR: &str = "127.0.0.1";
// the reason we use the ENR dummy values is because, due to the nature of the `--enr-match` flag, these will eventually become ENR ports (as well as listening ports).
let udp4_port = DUMMY_ENR_UDP_PORT;
let tcp4_port = DUMMY_ENR_TCP_PORT;
let ipv4_addr = IPV4_ADDR.parse::<Ipv4Addr>().unwrap(); let ipv4_addr = IPV4_ADDR.parse::<Ipv4Addr>().unwrap();
let udp4_port = unused_udp4_port().expect("Unable to find unused port.");
let tcp4_port = unused_tcp4_port().expect("Unable to find unused port.");
CommandLineTest::new() CommandLineTest::new()
.flag("enr-match", None) .flag("enr-match", None)
.flag("listen-address", Some(IPV4_ADDR)) .flag("listen-address", Some(IPV4_ADDR))
@ -1431,41 +1484,53 @@ fn enr_match_flag_over_ipv4_and_ipv6() {
config.network.enr_address, config.network.enr_address,
(Some(ipv4_addr), Some(ipv6_addr)) (Some(ipv4_addr), Some(ipv6_addr))
); );
assert_eq!(config.network.enr_udp6_port, Some(udp6_port)); assert_eq!(
assert_eq!(config.network.enr_udp4_port, Some(udp4_port)); config.network.enr_udp6_port.map(|port| port.get()),
Some(udp6_port)
);
assert_eq!(
config.network.enr_udp4_port.map(|port| port.get()),
Some(udp4_port)
);
}); });
} }
#[test] #[test]
fn enr_address_flag_with_ipv4() { fn enr_address_flag_with_ipv4() {
let addr = "192.167.1.1".parse::<Ipv4Addr>().unwrap(); let addr = "192.167.1.1".parse::<Ipv4Addr>().unwrap();
let port = unused_udp4_port().expect("Unable to find unused port."); let port = DUMMY_ENR_UDP_PORT;
CommandLineTest::new() CommandLineTest::new()
.flag("enr-address", Some("192.167.1.1")) .flag("enr-address", Some("192.167.1.1"))
.flag("enr-udp-port", Some(port.to_string().as_str())) .flag("enr-udp-port", Some(port.to_string().as_str()))
.run_with_zero_port() .run_with_zero_port()
.with_config(|config| { .with_config(|config| {
assert_eq!(config.network.enr_address, (Some(addr), None)); assert_eq!(config.network.enr_address, (Some(addr), None));
assert_eq!(config.network.enr_udp4_port, Some(port)); assert_eq!(
config.network.enr_udp4_port.map(|port| port.get()),
Some(port)
);
}); });
} }
#[test] #[test]
fn enr_address_flag_with_ipv6() { fn enr_address_flag_with_ipv6() {
let addr = "192.167.1.1".parse::<Ipv4Addr>().unwrap(); let addr = "192.167.1.1".parse::<Ipv4Addr>().unwrap();
let port = unused_udp4_port().expect("Unable to find unused port."); let port = DUMMY_ENR_UDP_PORT;
CommandLineTest::new() CommandLineTest::new()
.flag("enr-address", Some("192.167.1.1")) .flag("enr-address", Some("192.167.1.1"))
.flag("enr-udp-port", Some(port.to_string().as_str())) .flag("enr-udp-port", Some(port.to_string().as_str()))
.run_with_zero_port() .run_with_zero_port()
.with_config(|config| { .with_config(|config| {
assert_eq!(config.network.enr_address, (Some(addr), None)); assert_eq!(config.network.enr_address, (Some(addr), None));
assert_eq!(config.network.enr_udp4_port, Some(port)); assert_eq!(
config.network.enr_udp4_port.map(|port| port.get()),
Some(port)
);
}); });
} }
#[test] #[test]
fn enr_address_dns_flag() { fn enr_address_dns_flag() {
let addr = Ipv4Addr::LOCALHOST; let addr = Ipv4Addr::LOCALHOST;
let ipv6addr = Ipv6Addr::LOCALHOST; let ipv6addr = Ipv6Addr::LOCALHOST;
let port = unused_udp4_port().expect("Unable to find unused port."); let port = DUMMY_ENR_UDP_PORT;
CommandLineTest::new() CommandLineTest::new()
.flag("enr-address", Some("localhost")) .flag("enr-address", Some("localhost"))
.flag("enr-udp-port", Some(port.to_string().as_str())) .flag("enr-udp-port", Some(port.to_string().as_str()))
@ -1475,7 +1540,10 @@ fn enr_address_dns_flag() {
config.network.enr_address.0 == Some(addr) config.network.enr_address.0 == Some(addr)
|| config.network.enr_address.1 == Some(ipv6addr) || config.network.enr_address.1 == Some(ipv6addr)
); );
assert_eq!(config.network.enr_udp4_port, Some(port)); assert_eq!(
config.network.enr_udp4_port.map(|port| port.get()),
Some(port)
);
}); });
} }
#[test] #[test]
@ -1514,8 +1582,8 @@ fn http_address_ipv6_flag() {
} }
#[test] #[test]
fn http_port_flag() { fn http_port_flag() {
let port1 = unused_tcp4_port().expect("Unable to find unused port."); let port1 = 0;
let port2 = unused_tcp4_port().expect("Unable to find unused port."); let port2 = 0;
CommandLineTest::new() CommandLineTest::new()
.flag("http", None) .flag("http", None)
.flag("http-port", Some(port1.to_string().as_str())) .flag("http-port", Some(port1.to_string().as_str()))
@ -1671,8 +1739,8 @@ fn metrics_address_ipv6_flag() {
} }
#[test] #[test]
fn metrics_port_flag() { fn metrics_port_flag() {
let port1 = unused_tcp4_port().expect("Unable to find unused port."); let port1 = 0;
let port2 = unused_tcp4_port().expect("Unable to find unused port."); let port2 = 0;
CommandLineTest::new() CommandLineTest::new()
.flag("metrics", None) .flag("metrics", None)
.flag("metrics-port", Some(port1.to_string().as_str())) .flag("metrics-port", Some(port1.to_string().as_str()))

View File

@ -66,8 +66,8 @@ impl<E: EthSpec> LocalNetwork<E> {
BOOTNODE_PORT, BOOTNODE_PORT,
QUIC_PORT, QUIC_PORT,
); );
beacon_config.network.enr_udp4_port = Some(BOOTNODE_PORT); beacon_config.network.enr_udp4_port = Some(BOOTNODE_PORT.try_into().expect("non zero"));
beacon_config.network.enr_tcp4_port = Some(BOOTNODE_PORT); beacon_config.network.enr_tcp4_port = Some(BOOTNODE_PORT.try_into().expect("non zero"));
beacon_config.network.discv5_config.table_filter = |_| true; beacon_config.network.discv5_config.table_filter = |_| true;
let execution_node = if let Some(el_config) = &mut beacon_config.execution_layer { let execution_node = if let Some(el_config) = &mut beacon_config.execution_layer {
@ -152,14 +152,16 @@ impl<E: EthSpec> LocalNetwork<E> {
.expect("bootnode must have a network"), .expect("bootnode must have a network"),
); );
let count = (self.beacon_node_count() + self.proposer_node_count()) as u16; let count = (self.beacon_node_count() + self.proposer_node_count()) as u16;
let libp2p_tcp_port = BOOTNODE_PORT + count;
let discv5_port = BOOTNODE_PORT + count;
beacon_config.network.set_ipv4_listening_address( beacon_config.network.set_ipv4_listening_address(
std::net::Ipv4Addr::UNSPECIFIED, std::net::Ipv4Addr::UNSPECIFIED,
BOOTNODE_PORT + count, libp2p_tcp_port,
BOOTNODE_PORT + count, discv5_port,
QUIC_PORT + count, QUIC_PORT + count,
); );
beacon_config.network.enr_udp4_port = Some(BOOTNODE_PORT + count); beacon_config.network.enr_udp4_port = Some(discv5_port.try_into().unwrap());
beacon_config.network.enr_tcp4_port = Some(BOOTNODE_PORT + count); beacon_config.network.enr_tcp4_port = Some(libp2p_tcp_port.try_into().unwrap());
beacon_config.network.discv5_config.table_filter = |_| true; beacon_config.network.discv5_config.table_filter = |_| true;
beacon_config.network.proposer_only = is_proposer; beacon_config.network.proposer_only = is_proposer;
} }

View File

@ -1,7 +1,7 @@
#!/bin/bash #!/bin/bash
openssl req -x509 -sha256 -nodes -days 36500 -newkey rsa:4096 -keyout web3signer/key.key -out web3signer/cert.pem -config web3signer/config && openssl req -x509 -sha256 -nodes -days 36500 -newkey rsa:4096 -keyout web3signer/key.key -out web3signer/cert.pem -config web3signer/config &&
openssl pkcs12 -export -out web3signer/key.p12 -inkey web3signer/key.key -in web3signer/cert.pem -password pass:$(cat web3signer/password.txt) && openssl pkcs12 -export -aes256 -out web3signer/key.p12 -inkey web3signer/key.key -in web3signer/cert.pem -password pass:$(cat web3signer/password.txt) &&
cp web3signer/cert.pem lighthouse/web3signer.pem && cp web3signer/cert.pem lighthouse/web3signer.pem &&
openssl req -x509 -sha256 -nodes -days 36500 -newkey rsa:4096 -keyout lighthouse/key.key -out lighthouse/cert.pem -config lighthouse/config && openssl req -x509 -sha256 -nodes -days 36500 -newkey rsa:4096 -keyout lighthouse/key.key -out lighthouse/cert.pem -config lighthouse/config &&
openssl pkcs12 -export -out lighthouse/key.p12 -inkey lighthouse/key.key -in lighthouse/cert.pem -password pass:$(cat lighthouse/password.txt) && openssl pkcs12 -export -aes256 -out lighthouse/key.p12 -inkey lighthouse/key.key -in lighthouse/cert.pem -password pass:$(cat lighthouse/password.txt) &&
openssl x509 -noout -fingerprint -sha256 -inform pem -in lighthouse/cert.pem | cut -b 20-| sed "s/^/lighthouse /" > web3signer/known_clients.txt openssl x509 -noout -fingerprint -sha256 -inform pem -in lighthouse/cert.pem | cut -b 20-| sed "s/^/lighthouse /" > web3signer/known_clients.txt

View File

@ -1,33 +1,33 @@
-----BEGIN CERTIFICATE----- -----BEGIN CERTIFICATE-----
MIIFujCCA6KgAwIBAgIUELASgYwStCn/u/8tPByRADyCwLEwDQYJKoZIhvcNAQEL MIIFujCCA6KgAwIBAgIUXZijYo8W4/9dAq58ocFEbZDxohwwDQYJKoZIhvcNAQEL
BQAwazELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAlZBMREwDwYDVQQHDAhTb21lQ2l0 BQAwazELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAlZBMREwDwYDVQQHDAhTb21lQ2l0
eTESMBAGA1UECgwJTXlDb21wYW55MRMwEQYDVQQLDApNeURpdmlzaW9uMRMwEQYD eTESMBAGA1UECgwJTXlDb21wYW55MRMwEQYDVQQLDApNeURpdmlzaW9uMRMwEQYD
VQQDDApsaWdodGhvdXNlMCAXDTIzMDkyMjAzMDA1N1oYDzIxMjMwODI5MDMwMDU3 VQQDDApsaWdodGhvdXNlMCAXDTIzMDkyMDAyNTYzNloYDzIxMjMwODI3MDI1NjM2
WjBrMQswCQYDVQQGEwJVUzELMAkGA1UECAwCVkExETAPBgNVBAcMCFNvbWVDaXR5 WjBrMQswCQYDVQQGEwJVUzELMAkGA1UECAwCVkExETAPBgNVBAcMCFNvbWVDaXR5
MRIwEAYDVQQKDAlNeUNvbXBhbnkxEzARBgNVBAsMCk15RGl2aXNpb24xEzARBgNV MRIwEAYDVQQKDAlNeUNvbXBhbnkxEzARBgNVBAsMCk15RGl2aXNpb24xEzARBgNV
BAMMCmxpZ2h0aG91c2UwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCc BAMMCmxpZ2h0aG91c2UwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC1
i30cib5B/B5QNd8grzi4LxmlyfZFi3VfpukwdwOD1Xk3ODk1OtjAzhK46YhDclvc R1M9NnRwUsqFvJzNWPKuY1PW7llwRRWCixiWNvcxukGTa6AMLZDrYO1Y7qlw5m52
u98m1Dnib1Z+eTjRuEEoekIxz2+BbOle7G52LNvuDZpD+HKucqIU3TnEKPPuTYPp aHSA2fs2KyeA61yajG/BsLn1vmTtJMZXgLsG0MIqvhgOoh+ZZbl8biO0gQJSRSDE
lZ1n/9EyxXUwD5uTkn7xXzK8UFXUt73j6I6VFMdHlNcwLcx8KSwBDzvnGT4ew/UL jf0ogUVM9TCEt6ydbGnzgs8EESqvyXcreaXfmLI7jiX/BkwCdf+Ru+H3MF96QgAw
+ThON3j5rIT+nFHDcC2zoM+6ANdVkL6GHid4/cOcYW6GxB9TRZtEasqze41bC+kX Oz1d8/fxYJvIpT/DOx4NuMZouSAcUVXgwcVb6JXeTg0xVcL33lluquhYDR0gD5Fe
ZtPlV5V2nilAzVj8z9ynwBpHkLH+E6sMUhSEwA++QfI1gGf0FmSBgSIZ3RdPo/dp V0fPth+e9XMAH7udim8E5wn2Ep8CAVoeVq6K9mBM3NqP7+2YmU//jLbkd6UvKPaI
hkLG8fZXKMkMzKkRm5hcstDP6DnTIYl+CfuVez5gZ0/yelAqXNvTqMKuDhHTTRRY 0vps1zF9Bo8QewiRbM0IRse99ikCVZcjOcZSitw3kwTg59NjZ0Vk9R/2YQt/gGWM
aOXZX4BAiQO2Q6a6WYLe87E2ka5AF2T2y/BPeXjUwDS/1mFIB3FUGlMLVJt8/RLz VcR//EtbOZGqzGrLPFKOcWO85Ggz746Saj15N+bqT20hXHyiwYL8DLgJkMR2W9Nr
nXVGoSsYapttiiPucQbMPEysCJ4/LZ9zxe3EDWWjpurLHGi/Y/dVziEvg1Eoycix 67Vyi9SWSM6rdRQlezlHq/yNEh+JuY7eoC3VeVw9K1ZXP+OKAwbpcnvd3uLwV91f
dZogKz0QVCz4++QI0kPDDX7So7CWni2JJuYguF/8CX8QbCT2L8jXf0uQrq76FLKj kpT6kjc6d2h4bK8fhvF16Em42JypQCl0xMhgg/8MFO+6ZLy5otWAdsSYyO5k9CAa
88A7lS8DzXBt/pRryiIlDyLenJwHmrv6p+P/FYvgnJHvAEtTynxYm5GA16YWy+Dj 3zLeqd89dS7HNLdLZ0Y5SFWm6y5Kqu89ErIENafX5DxupHWsruiBV7zhDHNPaGcf
c5XVgNHjV4TdX3GueAp+NhBBaHDFvYCbP/oXkRvNRQIDAQABo1QwUjALBgNVHQ8E TPFe8xuDYsi155veOfEiDh4g+X1qjL8x8OEDjgsM3QIDAQABo1QwUjALBgNVHQ8E
BAMCBDAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDwYDVR0RBAgwBocEfwAAATAdBgNV BAMCBDAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDwYDVR0RBAgwBocEfwAAATAdBgNV
HQ4EFgQUoeeF4G1qTRzLvO583qitbNDzr10wDQYJKoZIhvcNAQELBQADggIBAA9Y HQ4EFgQU6r7QHkcEsWhEZHpcMpGxwKXQL9swDQYJKoZIhvcNAQELBQADggIBACyO
YZP0pZLyovSnjyyuTR4KE9B+TSwqHe/LvH+7EAXLH+cwhyS7ADfJyt3mOCbKHZSo 8xzqotye1J6xhDQCQnQF3dXaPTqfT31Ypg8UeU25V9N+bZO04CJKlOblukuvkedE
dmJ5KWQ6M2Xn9Wq40BPk8mQPmAxy0nHg5beG03HYXOIsK8zgXTMad1+D1jnHPAda x1RDeqG3A81D4JOgTGFmFVoEF4iTk3NBrsHuMzph6ImHTd3TD+5iG5a3GL0i9PAI
ldXJ2Y+ljx4TDXKCWpTaq1+flqgRD3t98tOLuiULZ5jsTFX8Xbun7matcjziU5Lo dHTT6z6t2wlayjmHotqQ+N4A4msx8IPBRULcCmId319gpSDHsvt2wYbLdh+d9E2h
GWVQPWkb8Vx+3QyfbfiYJ7hggfYTxQsVJOXKuD8k2FMtKn5oTp3VwD2kY1q2X2Yk vI0VleJpJ7eoy05842VTkFJebriSpi75yFphKUnyAKlONiMN3o6eg90wpWdI+1rQ
HsDZJdYrvjWi2LcZDKoSNeusuLrv1XoUnwsAa3ng6drvoEU16vfILLYqH820UJ61 js5lfm+pxYw8H6eSf+rl30m+amrxUlooqrSCHNVSO2c4+W5m/r3JfOiRqVUTxaO8
/fFm3a9BFHRvPVd/WcSeIVc9jx9+32RIVxlppwCINnGMGE20kUZxu0TiMjTX9bCp 0f/xYXo6SdRxdvJV18LEzOHURvkbqBjLoEfHbCC2EApevWAeCdjhvCBPl1IJZtFP
AouDuhwMt7z5jiQIi/CMxN6IlHBeVLqyK8ayWvH40xYgZTXlePpmLcQhcieNk7oJ sYDpYtHhw69JmZ7Nj75cQyRtJMQ5S4GsJ/haYXNZPgRL1XBo1ntuc8K1cLZ2MucQ
ard9jMfj4JhH5GbLXVptMBVJ0f9Ql4rW3EyNipvVKdkgTUNIeVm7LyUK220aT7ty 1170+2pi3IvwmST+/+7+2fyms1AwF7rj2dVxNfPIvOxi6E9lHmPVxvpbuOYOEhex
a0pGWHHViiF1MjGExo0P3gjZIML32TjZWlG3Nts5NAiyXDo4f78VeLyZQ7efVkub XqTum/MjI17Qf6eoipk81ppCFtO9s3qNe9SBSjzYEYnsytaMdZSSjsOhE/IyYPHI
GpjMf89vrmPdQhssoFr8fRFQObDe7hgxkgeiw9jgHItJl2/MWAxfsHV18HwiBqGW SICMjWE13du03Z5xWwK9i3UiFq+hIPhBHFPGkNFMmkQtcyS9lj9R0tKUmWdFPNa8
QzaZR995YhU480jvA5XR8+EB6QUZeCEKunW8WK/F nuhxn5kLUMriv3zsdhMPUC4NwM5XsopdWcuSxfnt
-----END CERTIFICATE----- -----END CERTIFICATE-----

View File

@ -1,52 +1,52 @@
-----BEGIN PRIVATE KEY----- -----BEGIN PRIVATE KEY-----
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
GlPvUaKC1KX0CX1qT1anVZq9bSMTG+M= vc3Za4ClKSLmb3ZA9giTswYMev+3BQ==
-----END PRIVATE KEY----- -----END PRIVATE KEY-----

View File

@ -1,33 +1,33 @@
-----BEGIN CERTIFICATE----- -----BEGIN CERTIFICATE-----
MIIFujCCA6KgAwIBAgIUOVccYETgo2YpKO85U4XRKifK09kwDQYJKoZIhvcNAQEL MIIFujCCA6KgAwIBAgIUIP5CN0WpH5om1bGaFn17Xc5ITJIwDQYJKoZIhvcNAQEL
BQAwazELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAlZBMREwDwYDVQQHDAhTb21lQ2l0 BQAwazELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAlZBMREwDwYDVQQHDAhTb21lQ2l0
eTESMBAGA1UECgwJTXlDb21wYW55MRMwEQYDVQQLDApNeURpdmlzaW9uMRMwEQYD eTESMBAGA1UECgwJTXlDb21wYW55MRMwEQYDVQQLDApNeURpdmlzaW9uMRMwEQYD
VQQDDAp3ZWIzc2lnbmVyMCAXDTIzMDkyMjAzMDA1NloYDzIxMjMwODI5MDMwMDU2 VQQDDAp3ZWIzc2lnbmVyMCAXDTIzMDkyMDAyNTYzNFoYDzIxMjMwODI3MDI1NjM0
WjBrMQswCQYDVQQGEwJVUzELMAkGA1UECAwCVkExETAPBgNVBAcMCFNvbWVDaXR5 WjBrMQswCQYDVQQGEwJVUzELMAkGA1UECAwCVkExETAPBgNVBAcMCFNvbWVDaXR5
MRIwEAYDVQQKDAlNeUNvbXBhbnkxEzARBgNVBAsMCk15RGl2aXNpb24xEzARBgNV MRIwEAYDVQQKDAlNeUNvbXBhbnkxEzARBgNVBAsMCk15RGl2aXNpb24xEzARBgNV
BAMMCndlYjNzaWduZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCr BAMMCndlYjNzaWduZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDS
JajqnvRQEREph+zu7rw1QjHQG1x2H44SJSMjX1Wzi9FErlRSOzywPFL2AzGsNvNS cvshqu3747j4KMaGyGW0CA2GAznogVyKqNt4lan/8mdYUI2PUeezaUOnmoyM9oWz
tPmxN/kF9mBjQIQHxo90M4GcZgW1aljPaXLvQWFrP9ak+JjHuUG+j51fVJp8F2Qc 1FPflpj7pVWagWlSOgZ9vOElqQhe+la4ZEdGmOpe44c1rBoeHK314Gbmr2EuCxaa
BG8i2LjjSLvkEYSULHI0kbMPws+DKcemvZJ6IhkoPkbtnx5Z1zDj8D6vvWGJguMO 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
-----END CERTIFICATE----- -----END CERTIFICATE-----

View File

@ -1,33 +1,33 @@
-----BEGIN CERTIFICATE----- -----BEGIN CERTIFICATE-----
MIIFujCCA6KgAwIBAgIUOVccYETgo2YpKO85U4XRKifK09kwDQYJKoZIhvcNAQEL MIIFujCCA6KgAwIBAgIUIP5CN0WpH5om1bGaFn17Xc5ITJIwDQYJKoZIhvcNAQEL
BQAwazELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAlZBMREwDwYDVQQHDAhTb21lQ2l0 BQAwazELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAlZBMREwDwYDVQQHDAhTb21lQ2l0
eTESMBAGA1UECgwJTXlDb21wYW55MRMwEQYDVQQLDApNeURpdmlzaW9uMRMwEQYD eTESMBAGA1UECgwJTXlDb21wYW55MRMwEQYDVQQLDApNeURpdmlzaW9uMRMwEQYD
VQQDDAp3ZWIzc2lnbmVyMCAXDTIzMDkyMjAzMDA1NloYDzIxMjMwODI5MDMwMDU2 VQQDDAp3ZWIzc2lnbmVyMCAXDTIzMDkyMDAyNTYzNFoYDzIxMjMwODI3MDI1NjM0
WjBrMQswCQYDVQQGEwJVUzELMAkGA1UECAwCVkExETAPBgNVBAcMCFNvbWVDaXR5 WjBrMQswCQYDVQQGEwJVUzELMAkGA1UECAwCVkExETAPBgNVBAcMCFNvbWVDaXR5
MRIwEAYDVQQKDAlNeUNvbXBhbnkxEzARBgNVBAsMCk15RGl2aXNpb24xEzARBgNV MRIwEAYDVQQKDAlNeUNvbXBhbnkxEzARBgNVBAsMCk15RGl2aXNpb24xEzARBgNV
BAMMCndlYjNzaWduZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCr BAMMCndlYjNzaWduZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDS
JajqnvRQEREph+zu7rw1QjHQG1x2H44SJSMjX1Wzi9FErlRSOzywPFL2AzGsNvNS cvshqu3747j4KMaGyGW0CA2GAznogVyKqNt4lan/8mdYUI2PUeezaUOnmoyM9oWz
tPmxN/kF9mBjQIQHxo90M4GcZgW1aljPaXLvQWFrP9ak+JjHuUG+j51fVJp8F2Qc 1FPflpj7pVWagWlSOgZ9vOElqQhe+la4ZEdGmOpe44c1rBoeHK314Gbmr2EuCxaa
BG8i2LjjSLvkEYSULHI0kbMPws+DKcemvZJ6IhkoPkbtnx5Z1zDj8D6vvWGJguMO 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
-----END CERTIFICATE----- -----END CERTIFICATE-----

View File

@ -1,52 +1,52 @@
-----BEGIN PRIVATE KEY----- -----BEGIN PRIVATE KEY-----
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
CXDusRSlsQNpzHc6QKq8IDZLut9g oJ/aHbXf6PdOO9MYiI/es/dfKK8ywA==
-----END PRIVATE KEY----- -----END PRIVATE KEY-----

View File

@ -1 +1 @@
lighthouse FF:4C:84:A6:37:28:EC:7E:A7:D8:C6:49:0D:C6:F9:5D:C1:06:BA:6D:69:49:0A:AA:38:32:01:2B:ED:D9:F2:FA lighthouse 02:D0:A8:C0:6A:59:90:40:54:67:D4:BD:AE:5A:D4:F5:14:A9:79:38:98:E0:62:93:C1:77:13:FC:B4:60:65:CE

View File

@ -56,7 +56,7 @@ itertools = { workspace = true }
monitoring_api = { workspace = true } monitoring_api = { workspace = true }
sensitive_url = { workspace = true } sensitive_url = { workspace = true }
task_executor = { workspace = true } task_executor = { workspace = true }
reqwest = { workspace = true } reqwest = { workspace = true, features = ["native-tls"] }
url = { workspace = true } url = { workspace = true }
malloc_utils = { workspace = true } malloc_utils = { workspace = true }
sysinfo = { workspace = true } sysinfo = { workspace = true }

View File

@ -6,20 +6,23 @@ ARCHIVE_URL := https://github.com/eth-clients/slashing-protection-interchange-te
ifeq ($(OS),Windows_NT) ifeq ($(OS),Windows_NT)
ifeq (, $(shell where rm)) ifeq (, $(shell where rm))
rmfile = if exist $(1) (del /F /Q $(1)) rmfile = if exist $(1) (del /F /Q $(1))
rmdir = if exist $(1) (rmdir /Q /S $(1)) rmdir = if exist $(1) (rmdir /Q /S $(1))
makedir = if not exist $(1) (mkdir $(1))
else else
rmfile = rm -f $(1) rmfile = rm -f $(1)
rmdir = rm -rf $(1) rmdir = rm -rf $(1)
makedir = mkdir -p $(1)
endif endif
else else
rmfile = rm -f $(1) rmfile = rm -f $(1)
rmdir = rm -rf $(1) rmdir = rm -rf $(1)
makedir = mkdir -p $(1)
endif endif
$(OUTPUT_DIR): $(TARBALL) $(OUTPUT_DIR): $(TARBALL)
$(call rmdir,$@) $(call rmdir,$@)
mkdir $@ $(call makedir,$@)
tar --strip-components=1 -xzf $^ -C $@ tar --strip-components=1 -xzf $^ -C $@
$(TARBALL): $(TARBALL):

View File

@ -25,8 +25,10 @@ fn test_root_dir() -> PathBuf {
.join("tests") .join("tests")
} }
// NOTE: I've combined two tests together to avoid a race-condition which occurs when fighting over
// which test builds the TEST_ROOT_DIR lazy static.
#[test] #[test]
fn generated() { fn generated_and_with_minification() {
for entry in TEST_ROOT_DIR for entry in TEST_ROOT_DIR
.join("generated") .join("generated")
.read_dir() .read_dir()
@ -37,10 +39,7 @@ fn generated() {
let test_case: MultiTestCase = serde_json::from_reader(&file).unwrap(); let test_case: MultiTestCase = serde_json::from_reader(&file).unwrap();
test_case.run(false); test_case.run(false);
} }
}
#[test]
fn generated_with_minification() {
for entry in TEST_ROOT_DIR for entry in TEST_ROOT_DIR
.join("generated") .join("generated")
.read_dir() .read_dir()

View File

@ -1176,7 +1176,8 @@ pub fn serve<T: 'static + SlotClock + Clone, E: EthSpec>(
.or(get_fee_recipient) .or(get_fee_recipient)
.or(get_gas_limit) .or(get_gas_limit)
.or(get_std_keystores) .or(get_std_keystores)
.or(get_std_remotekeys), .or(get_std_remotekeys)
.recover(warp_utils::reject::handle_rejection),
) )
.or(warp::post().and( .or(warp::post().and(
post_validators post_validators
@ -1187,15 +1188,18 @@ pub fn serve<T: 'static + SlotClock + Clone, E: EthSpec>(
.or(post_fee_recipient) .or(post_fee_recipient)
.or(post_gas_limit) .or(post_gas_limit)
.or(post_std_keystores) .or(post_std_keystores)
.or(post_std_remotekeys), .or(post_std_remotekeys)
.recover(warp_utils::reject::handle_rejection),
)) ))
.or(warp::patch().and(patch_validators)) .or(warp::patch()
.and(patch_validators.recover(warp_utils::reject::handle_rejection)))
.or(warp::delete().and( .or(warp::delete().and(
delete_lighthouse_keystores delete_lighthouse_keystores
.or(delete_fee_recipient) .or(delete_fee_recipient)
.or(delete_gas_limit) .or(delete_gas_limit)
.or(delete_std_keystores) .or(delete_std_keystores)
.or(delete_std_remotekeys), .or(delete_std_remotekeys)
.recover(warp_utils::reject::handle_rejection),
)), )),
) )
// The auth route and logs are the only routes that are allowed to be accessed without the API token. // The auth route and logs are the only routes that are allowed to be accessed without the API token.

View File

@ -2146,7 +2146,7 @@ async fn import_remotekey_web3signer_enabled() {
assert_eq!(tester.vals_total(), 1); assert_eq!(tester.vals_total(), 1);
assert_eq!(tester.vals_enabled(), 1); assert_eq!(tester.vals_enabled(), 1);
let vals = tester.initialized_validators.read(); let vals = tester.initialized_validators.read();
let web3_vals = vals.validator_definitions().clone(); let web3_vals = vals.validator_definitions();
// Import remotekeys. // Import remotekeys.
let import_res = tester let import_res = tester
@ -2164,7 +2164,7 @@ async fn import_remotekey_web3signer_enabled() {
assert_eq!(tester.vals_total(), 1); assert_eq!(tester.vals_total(), 1);
assert_eq!(tester.vals_enabled(), 1); assert_eq!(tester.vals_enabled(), 1);
let vals = tester.initialized_validators.read(); let vals = tester.initialized_validators.read();
let remote_vals = vals.validator_definitions().clone(); let remote_vals = vals.validator_definitions();
// Web3signer should not be overwritten since it is enabled. // Web3signer should not be overwritten since it is enabled.
assert!(web3_vals == remote_vals); assert!(web3_vals == remote_vals);