Merge pull request #4781 from jimmygchen/merge-unstable-to-deneb-20230926

Merge `unstable` into `deneb-free-blobs`
This commit is contained in:
realbigsean 2023-09-26 08:19:37 -04:00 committed by GitHub
commit a642bd7de7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
165 changed files with 3903 additions and 3065 deletions

View File

@ -57,7 +57,7 @@ jobs:
build-docker-single-arch: build-docker-single-arch:
name: build-docker-${{ matrix.binary }}${{ matrix.features.version_suffix }} name: build-docker-${{ matrix.binary }}${{ matrix.features.version_suffix }}
# 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", "large"]') || 'ubuntu-22.04' }} runs-on: ${{ github.repository == 'sigp/lighthouse' && fromJson('["self-hosted", "linux", "release"]') || 'ubuntu-22.04' }}
strategy: strategy:
matrix: matrix:
binary: [aarch64, binary: [aarch64,
@ -79,7 +79,7 @@ jobs:
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Update Rust - name: Update Rust
if: env.SELF_HOSTED_RUNNERS == false if: env.SELF_HOSTED_RUNNERS == 'false'
run: rustup update stable run: rustup update stable
- name: Dockerhub login - name: Dockerhub login
run: | run: |
@ -107,9 +107,11 @@ jobs:
run: echo "MODERNITY_SUFFIX=-modern" >> $GITHUB_ENV; run: echo "MODERNITY_SUFFIX=-modern" >> $GITHUB_ENV;
- name: Install QEMU - name: Install QEMU
if: env.SELF_HOSTED_RUNNERS == 'false'
run: sudo apt-get update && sudo apt-get install -y qemu-user-static run: sudo apt-get update && sudo apt-get install -y qemu-user-static
- name: Set up Docker Buildx - name: Set up Docker Buildx
if: env.SELF_HOSTED_RUNNERS == 'false'
uses: docker/setup-buildx-action@v2 uses: docker/setup-buildx-action@v2
- name: Build and push - name: Build and push

View File

@ -14,6 +14,8 @@ env:
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
REPO_NAME: ${{ github.repository_owner }}/lighthouse REPO_NAME: ${{ github.repository_owner }}/lighthouse
IMAGE_NAME: ${{ github.repository_owner }}/lighthouse IMAGE_NAME: ${{ github.repository_owner }}/lighthouse
# Enable self-hosted runners for the sigp repo only.
SELF_HOSTED_RUNNERS: ${{ github.repository == 'sigp/lighthouse' }}
jobs: jobs:
extract-version: extract-version:
@ -38,36 +40,37 @@ jobs:
x86_64-windows-portable] x86_64-windows-portable]
include: include:
- arch: aarch64-unknown-linux-gnu - arch: aarch64-unknown-linux-gnu
platform: ubuntu-latest runner: ${{ github.repository == 'sigp/lighthouse' && fromJson('["self-hosted", "linux", "release", "large"]') || 'ubuntu-latest' }}
profile: maxperf profile: maxperf
- arch: aarch64-unknown-linux-gnu-portable - arch: aarch64-unknown-linux-gnu-portable
platform: ubuntu-latest runner: ${{ github.repository == 'sigp/lighthouse' && fromJson('["self-hosted", "linux", "release", "large"]') || 'ubuntu-latest' }}
profile: maxperf profile: maxperf
- arch: x86_64-unknown-linux-gnu - arch: x86_64-unknown-linux-gnu
platform: ubuntu-latest runner: ${{ github.repository == 'sigp/lighthouse' && fromJson('["self-hosted", "linux", "release", "large"]') || 'ubuntu-latest' }}
profile: maxperf profile: maxperf
- arch: x86_64-unknown-linux-gnu-portable - arch: x86_64-unknown-linux-gnu-portable
platform: ubuntu-latest runner: ${{ github.repository == 'sigp/lighthouse' && fromJson('["self-hosted", "linux", "release", "large"]') || 'ubuntu-latest' }}
profile: maxperf profile: maxperf
- arch: x86_64-apple-darwin - arch: x86_64-apple-darwin
platform: macos-latest runner: macos-latest
profile: maxperf profile: maxperf
- arch: x86_64-apple-darwin-portable - arch: x86_64-apple-darwin-portable
platform: macos-latest runner: macos-latest
profile: maxperf profile: maxperf
- arch: x86_64-windows - arch: x86_64-windows
platform: windows-2019 runner: ${{ github.repository == 'sigp/lighthouse' && fromJson('["self-hosted", "windows", "release"]') || 'windows-2019' }}
profile: maxperf profile: maxperf
- arch: x86_64-windows-portable - arch: x86_64-windows-portable
platform: windows-2019 runner: ${{ github.repository == 'sigp/lighthouse' && fromJson('["self-hosted", "windows", "release"]') || 'windows-2019' }}
profile: maxperf profile: maxperf
runs-on: ${{ matrix.platform }} runs-on: ${{ matrix.runner }}
needs: extract-version needs: extract-version
steps: steps:
- name: Checkout sources - name: Checkout sources
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'
run: rustup update stable run: rustup update stable
# ============================== # ==============================
@ -75,7 +78,7 @@ jobs:
# ============================== # ==============================
- uses: KyleMayes/install-llvm-action@v1 - uses: KyleMayes/install-llvm-action@v1
if: startsWith(matrix.arch, 'x86_64-windows') if: env.SELF_HOSTED_RUNNERS == 'false' && startsWith(matrix.arch, 'x86_64-windows')
with: with:
version: "15.0" version: "15.0"
directory: ${{ runner.temp }}/llvm directory: ${{ runner.temp }}/llvm

View File

@ -58,7 +58,7 @@ jobs:
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", "large"]') || 'ubuntu-latest' }} runs-on: ${{ github.repository == 'sigp/lighthouse' && fromJson('["self-hosted", "linux", "CI", "large"]') || 'ubuntu-latest' }}
needs: cargo-fmt needs: cargo-fmt
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
@ -73,7 +73,7 @@ jobs:
run: make test-release run: make test-release
release-tests-windows: release-tests-windows:
name: release-tests-windows name: release-tests-windows
runs-on: ${{ github.repository == 'sigp/lighthouse' && fromJson('["self-hosted", "windows"]') || 'windows-2019' }} runs-on: ${{ github.repository == 'sigp/lighthouse' && fromJson('["self-hosted", "windows", "CI"]') || 'windows-2019' }}
needs: cargo-fmt needs: cargo-fmt
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
@ -98,7 +98,7 @@ jobs:
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", "large"]') || 'ubuntu-latest' }} runs-on: ${{ github.repository == 'sigp/lighthouse' && fromJson('["self-hosted", "linux", "CI", "large"]') || 'ubuntu-latest' }}
needs: cargo-fmt needs: cargo-fmt
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
@ -140,7 +140,7 @@ jobs:
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", "large"]') || 'ubuntu-latest' }} runs-on: ${{ github.repository == 'sigp/lighthouse' && fromJson('["self-hosted", "linux", "CI", "large"]') || 'ubuntu-latest' }}
needs: cargo-fmt needs: cargo-fmt
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
@ -166,7 +166,7 @@ jobs:
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", "small"]') || 'ubuntu-latest' }} runs-on: ${{ github.repository == 'sigp/lighthouse' && fromJson('["self-hosted", "linux", "CI", "small"]') || 'ubuntu-latest' }}
needs: cargo-fmt needs: cargo-fmt
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3

1067
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -90,10 +90,139 @@ members = [
] ]
resolver = "2" resolver = "2"
[patch] [workspace.package]
[patch.crates-io] edition = "2021"
# TODO: remove when 0.3.6 get's released.
warp = { git = "https://github.com/seanmonstar/warp.git", rev="149913fe" } [workspace.dependencies]
arbitrary = { version = "1", features = ["derive"] }
bincode = "1"
bitvec = "1"
byteorder = "1"
bytes = "1"
clap = "2"
compare_fields_derive = { path = "common/compare_fields_derive" }
criterion = "0.3"
delay_map = "0.3"
derivative = "2"
dirs = "3"
discv5 = { version = "0.3", features = ["libp2p"] }
env_logger = "0.9"
error-chain = "0.12"
ethereum-types = "0.14"
ethereum_hashing = "1.0.0-beta.2"
ethereum_serde_utils = "0.5"
ethereum_ssz = "0.5"
ethereum_ssz_derive = "0.5"
ethers-core = "1"
ethers-providers = { version = "1", default-features = false }
exit-future = "0.2"
fnv = "1"
fs2 = "0.4"
futures = "0.3"
hex = "0.4"
hyper = "0.14"
itertools = "0.10"
lazy_static = "1"
libsecp256k1 = "0.7"
log = "0.4"
lru = "0.7"
maplit = "1"
num_cpus = "1"
parking_lot = "0.12"
paste = "1"
quickcheck = "1"
quickcheck_macros = "1"
quote = "1"
r2d2 = "0.8"
rand = "0.8"
rayon = "1.7"
regex = "1"
reqwest = { version = "0.11", default-features = false, features = ["blocking", "json", "stream", "rustls-tls"] }
ring = "0.16"
rusqlite = { version = "0.28", features = ["bundled"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
serde_repr = "0.1"
serde_yaml = "0.8"
sha2 = "0.9"
slog = { version = "2", features = ["max_level_trace", "release_max_level_trace", "nested-values"] }
slog-async = "2"
slog-term = "2"
sloggers = { version = "2", features = ["json"] }
smallvec = "1"
snap = "1"
ssz_types = "0.5"
strum = { version = "0.24", features = ["derive"] }
superstruct = "0.6"
syn = "1"
sysinfo = "0.26"
tempfile = "3"
tokio = { version = "1", features = ["rt-multi-thread", "sync"] }
tokio-stream = { version = "0.1", features = ["sync"] }
tokio-util = { version = "0.6", features = ["codec", "compat", "time"] }
tree_hash = "0.5"
tree_hash_derive = "0.5"
url = "2"
uuid = { version = "0.8", features = ["serde", "v4"] }
# TODO update to warp 0.3.6 after released.
warp = { git = "https://github.com/seanmonstar/warp.git", default-features = false, features = ["tls"] }
zeroize = { version = "1", features = ["zeroize_derive"] }
zip = "0.6"
# Local crates.
account_utils = { path = "common/account_utils" }
beacon_chain = { path = "beacon_node/beacon_chain" }
beacon_node = { path = "beacon_node" }
beacon_processor = { path = "beacon_node/beacon_processor" }
bls = { path = "crypto/bls" }
cached_tree_hash = { path = "consensus/cached_tree_hash" }
clap_utils = { path = "common/clap_utils" }
compare_fields = { path = "common/compare_fields" }
deposit_contract = { path = "common/deposit_contract" }
directory = { path = "common/directory" }
environment = { path = "lighthouse/environment" }
eth1 = { path = "beacon_node/eth1" }
eth1_test_rig = { path = "testing/eth1_test_rig" }
eth2 = { path = "common/eth2" }
eth2_config = { path = "common/eth2_config" }
eth2_key_derivation = { path = "crypto/eth2_key_derivation" }
eth2_keystore = { path = "crypto/eth2_keystore" }
eth2_network_config = { path = "common/eth2_network_config" }
eth2_wallet = { path = "crypto/eth2_wallet" }
execution_layer = { path = "beacon_node/execution_layer" }
filesystem = { path = "common/filesystem" }
fork_choice = { path = "consensus/fork_choice" }
genesis = { path = "beacon_node/genesis" }
http_api = { path = "beacon_node/http_api" }
int_to_bytes = { path = "consensus/int_to_bytes" }
kzg = { path = "crypto/kzg" }
lighthouse_metrics = { path = "common/lighthouse_metrics" }
lighthouse_network = { path = "beacon_node/lighthouse_network" }
lighthouse_version = { path = "common/lighthouse_version" }
lockfile = { path = "common/lockfile" }
logging = { path = "common/logging" }
lru_cache = { path = "common/lru_cache" }
malloc_utils = { path = "common/malloc_utils" }
merkle_proof = { path = "consensus/merkle_proof" }
monitoring_api = { path = "common/monitoring_api" }
network = { path = "beacon_node/network" }
operation_pool = { path = "beacon_node/operation_pool" }
pretty_reqwest_error = { path = "common/pretty_reqwest_error" }
proto_array = { path = "consensus/proto_array" }
safe_arith = {path = "consensus/safe_arith"}
sensitive_url = { path = "common/sensitive_url" }
slasher = { path = "slasher" }
slashing_protection = { path = "validator_client/slashing_protection" }
slot_clock = { path = "common/slot_clock" }
state_processing = { path = "consensus/state_processing" }
store = { path = "beacon_node/store" }
swap_or_not_shuffle = { path = "consensus/swap_or_not_shuffle" }
task_executor = { path = "common/task_executor" }
types = { path = "consensus/types" }
unused_port = { path = "common/unused_port" }
validator_client = { path = "validator_client/" }
validator_dir = { path = "common/validator_dir" }
warp_utils = { path = "common/warp_utils" }
[profile.maxperf] [profile.maxperf]
inherits = "release" inherits = "release"

View File

@ -1,4 +1,4 @@
FROM rust:1.68.2-bullseye AS builder FROM rust:1.69.0-bullseye AS builder
RUN apt-get update && apt-get -y upgrade && apt-get install -y cmake libclang-dev RUN apt-get update && apt-get -y upgrade && apt-get install -y cmake libclang-dev
COPY . lighthouse COPY . lighthouse
ARG FEATURES ARG FEATURES

View File

@ -215,7 +215,7 @@ 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:
cargo install --force cargo-audit cargo install --force cargo-audit
cargo audit --ignore RUSTSEC-2023-0052 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.
vendor: vendor:

View File

@ -5,31 +5,31 @@ authors = [
"Paul Hauner <paul@paulhauner.com>", "Paul Hauner <paul@paulhauner.com>",
"Luke Anderson <luke@sigmaprime.io>", "Luke Anderson <luke@sigmaprime.io>",
] ]
edition = "2021" edition = { workspace = true }
[dependencies] [dependencies]
bls = { path = "../crypto/bls" } bls = { workspace = true }
clap = "2.33.3" clap = { workspace = true }
types = { path = "../consensus/types" } types = { workspace = true }
environment = { path = "../lighthouse/environment" } environment = { workspace = true }
eth2_network_config = { path = "../common/eth2_network_config" } eth2_network_config = { workspace = true }
clap_utils = { path = "../common/clap_utils" } clap_utils = { workspace = true }
directory = { path = "../common/directory" } directory = { workspace = true }
eth2_wallet = { path = "../crypto/eth2_wallet" } eth2_wallet = { workspace = true }
eth2_wallet_manager = { path = "../common/eth2_wallet_manager" } eth2_wallet_manager = { path = "../common/eth2_wallet_manager" }
validator_dir = { path = "../common/validator_dir" } validator_dir = { workspace = true }
tokio = { version = "1.14.0", features = ["full"] } tokio = { workspace = true }
eth2_keystore = { path = "../crypto/eth2_keystore" } eth2_keystore = { workspace = true }
account_utils = { path = "../common/account_utils" } account_utils = { workspace = true }
slashing_protection = { path = "../validator_client/slashing_protection" } slashing_protection = { workspace = true }
eth2 = { path = "../common/eth2" } eth2 = { workspace = true }
safe_arith = { path = "../consensus/safe_arith" } safe_arith = { workspace = true }
slot_clock = { path = "../common/slot_clock" } slot_clock = { workspace = true }
filesystem = { path = "../common/filesystem" } filesystem = { workspace = true }
sensitive_url = { path = "../common/sensitive_url" } sensitive_url = { workspace = true }
serde = { version = "1.0.116", features = ["derive"] } serde = { workspace = true }
serde_json = "1.0.58" serde_json = { workspace = true }
slog = { version = "2.5.2" } slog = { workspace = true }
[dev-dependencies] [dev-dependencies]
tempfile = "3.1.0" tempfile = { workspace = true }

View File

@ -10,7 +10,6 @@ use eth2_keystore::Keystore;
use eth2_network_config::Eth2NetworkConfig; use eth2_network_config::Eth2NetworkConfig;
use safe_arith::SafeArith; use safe_arith::SafeArith;
use sensitive_url::SensitiveUrl; use sensitive_url::SensitiveUrl;
use slog::Logger;
use slot_clock::{SlotClock, SystemTimeSlotClock}; use slot_clock::{SlotClock, SystemTimeSlotClock};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::time::Duration; use std::time::Duration;
@ -79,12 +78,6 @@ pub fn cli_run<E: EthSpec>(matches: &ArgMatches, env: Environment<E>) -> Result<
let password_file_path: Option<PathBuf> = let password_file_path: Option<PathBuf> =
clap_utils::parse_optional(matches, PASSWORD_FILE_FLAG)?; clap_utils::parse_optional(matches, PASSWORD_FILE_FLAG)?;
let genesis_state_url: Option<String> =
clap_utils::parse_optional(matches, "genesis-state-url")?;
let genesis_state_url_timeout =
clap_utils::parse_required(matches, "genesis-state-url-timeout")
.map(Duration::from_secs)?;
let stdin_inputs = cfg!(windows) || matches.is_present(STDIN_INPUTS_FLAG); let stdin_inputs = cfg!(windows) || matches.is_present(STDIN_INPUTS_FLAG);
let no_wait = matches.is_present(NO_WAIT); let no_wait = matches.is_present(NO_WAIT);
let no_confirmation = matches.is_present(NO_CONFIRMATION); let no_confirmation = matches.is_present(NO_CONFIRMATION);
@ -111,9 +104,6 @@ pub fn cli_run<E: EthSpec>(matches: &ArgMatches, env: Environment<E>) -> Result<
&eth2_network_config, &eth2_network_config,
no_wait, no_wait,
no_confirmation, no_confirmation,
genesis_state_url,
genesis_state_url_timeout,
env.core_context().log(),
))?; ))?;
Ok(()) Ok(())
@ -130,13 +120,10 @@ async fn publish_voluntary_exit<E: EthSpec>(
eth2_network_config: &Eth2NetworkConfig, eth2_network_config: &Eth2NetworkConfig,
no_wait: bool, no_wait: bool,
no_confirmation: bool, no_confirmation: bool,
genesis_state_url: Option<String>,
genesis_state_url_timeout: Duration,
log: &Logger,
) -> Result<(), String> { ) -> Result<(), String> {
let genesis_data = get_geneisis_data(client).await?; let genesis_data = get_geneisis_data(client).await?;
let testnet_genesis_root = eth2_network_config let testnet_genesis_root = eth2_network_config
.genesis_validators_root::<E>(genesis_state_url.as_deref(), genesis_state_url_timeout, log)? .genesis_validators_root::<E>()?
.ok_or("Genesis state is unknown")?; .ok_or("Genesis state is unknown")?;
// Verify that the beacon node and validator being exited are on the same network. // Verify that the beacon node and validator being exited are on the same network.

View File

@ -7,7 +7,6 @@ use slashing_protection::{
use std::fs::File; use std::fs::File;
use std::path::PathBuf; use std::path::PathBuf;
use std::str::FromStr; use std::str::FromStr;
use std::time::Duration;
use types::{Epoch, EthSpec, PublicKeyBytes, Slot}; use types::{Epoch, EthSpec, PublicKeyBytes, Slot};
pub const CMD: &str = "slashing-protection"; pub const CMD: &str = "slashing-protection";
@ -82,24 +81,12 @@ pub fn cli_run<T: EthSpec>(
validator_base_dir: PathBuf, validator_base_dir: PathBuf,
) -> Result<(), String> { ) -> Result<(), String> {
let slashing_protection_db_path = validator_base_dir.join(SLASHING_PROTECTION_FILENAME); let slashing_protection_db_path = validator_base_dir.join(SLASHING_PROTECTION_FILENAME);
let genesis_state_url: Option<String> =
clap_utils::parse_optional(matches, "genesis-state-url")?;
let genesis_state_url_timeout =
clap_utils::parse_required(matches, "genesis-state-url-timeout")
.map(Duration::from_secs)?;
let context = env.core_context();
let eth2_network_config = env let eth2_network_config = env
.eth2_network_config .eth2_network_config
.ok_or("Unable to get testnet configuration from the environment")?; .ok_or("Unable to get testnet configuration from the environment")?;
let genesis_validators_root = eth2_network_config let genesis_validators_root = eth2_network_config
.genesis_validators_root::<T>( .genesis_validators_root::<T>()?
genesis_state_url.as_deref(),
genesis_state_url_timeout,
context.log(),
)?
.ok_or_else(|| "Unable to get genesis state, has genesis occurred?".to_string())?; .ok_or_else(|| "Unable to get genesis state, has genesis occurred?".to_string())?;
match matches.subcommand() { match matches.subcommand() {

View File

@ -1,11 +1,11 @@
[package] [package]
name = "beacon_node" name = "beacon_node"
version = "4.4.1" version = "4.5.0"
authors = [ authors = [
"Paul Hauner <paul@paulhauner.com>", "Paul Hauner <paul@paulhauner.com>",
"Age Manning <Age@AgeManning.com", "Age Manning <Age@AgeManning.com",
] ]
edition = "2021" edition = { workspace = true }
[lib] [lib]
name = "beacon_node" name = "beacon_node"
@ -20,34 +20,31 @@ write_ssz_files = [
] # Writes debugging .ssz files to /tmp during block processing. ] # Writes debugging .ssz files to /tmp during block processing.
[dependencies] [dependencies]
eth2_config = { path = "../common/eth2_config" } eth2_config = { workspace = true }
beacon_chain = { path = "beacon_chain" } beacon_chain = { workspace = true }
types = { path = "../consensus/types" } types = { workspace = true }
store = { path = "./store" } store = { workspace = true }
client = { path = "client" } client = { path = "client" }
clap = "2.33.3" clap = { workspace = true }
slog = { version = "2.5.2", features = [ slog = { workspace = true }
"max_level_trace", dirs = { workspace = true }
"release_max_level_trace", directory = { workspace = true }
] } futures = { workspace = true }
dirs = "3.0.1" environment = { workspace = true }
directory = { path = "../common/directory" } task_executor = { workspace = true }
futures = "0.3.7" genesis = { workspace = true }
environment = { path = "../lighthouse/environment" } eth2_network_config = { workspace = true }
task_executor = { path = "../common/task_executor" } execution_layer = { workspace = true }
genesis = { path = "genesis" } lighthouse_network = { workspace = true }
eth2_network_config = { path = "../common/eth2_network_config" } serde = { workspace = true }
execution_layer = { path = "execution_layer" } serde_json = { workspace = true }
lighthouse_network = { path = "./lighthouse_network" } clap_utils = { workspace = true }
serde = "1.0.116" hyper = { workspace = true }
serde_json = "1.0.58" lighthouse_version = { workspace = true }
clap_utils = { path = "../common/clap_utils" } hex = { workspace = true }
hyper = "0.14.4" slasher = { workspace = true }
lighthouse_version = { path = "../common/lighthouse_version" } monitoring_api = { workspace = true }
hex = "0.4.2" sensitive_url = { workspace = true }
slasher = { path = "../slasher" } http_api = { workspace = true }
monitoring_api = { path = "../common/monitoring_api" } unused_port = { workspace = true }
sensitive_url = { path = "../common/sensitive_url" } strum = { workspace = true }
http_api = { path = "http_api" }
unused_port = { path = "../common/unused_port" }
strum = "0.24.1"

View File

@ -2,7 +2,7 @@
name = "beacon_chain" name = "beacon_chain"
version = "0.2.0" version = "0.2.0"
authors = ["Paul Hauner <paul@paulhauner.com>", "Age Manning <Age@AgeManning.com>"] authors = ["Paul Hauner <paul@paulhauner.com>", "Age Manning <Age@AgeManning.com>"]
edition = "2021" edition = { workspace = true }
autotests = false # using a single test binary compiles faster autotests = false # using a single test binary compiles faster
[features] [features]
@ -12,66 +12,64 @@ participation_metrics = [] # Exposes validator participation metrics to Prometh
fork_from_env = [] # Initialise the harness chain spec from the FORK_NAME env variable fork_from_env = [] # Initialise the harness chain spec from the FORK_NAME env variable
[dev-dependencies] [dev-dependencies]
maplit = "1.0.2" maplit = { workspace = true }
environment = { path = "../../lighthouse/environment" } environment = { workspace = true }
serde_json = "1.0.58" serde_json = { workspace = true }
[dependencies] [dependencies]
serde_json = "1.0.58" serde_json = { workspace = true }
eth2_network_config = { path = "../../common/eth2_network_config"} eth2_network_config = { workspace = true }
merkle_proof = { path = "../../consensus/merkle_proof" } merkle_proof = { workspace = true }
store = { path = "../store" } store = { workspace = true }
parking_lot = "0.12.0" parking_lot = { workspace = true }
lazy_static = "1.4.0" lazy_static = { workspace = true }
smallvec = "1.6.1" smallvec = { workspace = true }
lighthouse_metrics = { path = "../../common/lighthouse_metrics" } lighthouse_metrics = { workspace = true }
operation_pool = { path = "../operation_pool" } operation_pool = { workspace = true }
rayon = "1.4.1" rayon = { workspace = true }
serde = "1.0.116" serde = { workspace = true }
serde_derive = "1.0.116" ethereum_serde_utils = { workspace = true }
ethereum_serde_utils = "0.5.0" slog = { workspace = true }
slog = { version = "2.5.2", features = ["max_level_trace"] } sloggers = { workspace = true }
sloggers = { version = "2.1.1", features = ["json"] } slot_clock = { workspace = true }
slot_clock = { path = "../../common/slot_clock" } ethereum_hashing = { workspace = true }
ethereum_hashing = "1.0.0-beta.2" ethereum_ssz = { workspace = true }
ethereum_ssz = "0.5.0" ssz_types = { workspace = true }
ssz_types = "0.5.4" ethereum_ssz_derive = { workspace = true }
ethereum_ssz_derive = "0.5.3" state_processing = { workspace = true }
state_processing = { path = "../../consensus/state_processing" } tree_hash_derive = { workspace = true }
tree_hash_derive = "0.5.0" tree_hash = { workspace = true }
tree_hash = "0.5.2" types = { workspace = true }
types = { path = "../../consensus/types" } tokio = { workspace = true }
tokio = "1.14.0" tokio-stream = { workspace = true }
tokio-stream = "0.1.3" eth1 = { workspace = true }
eth1 = { path = "../eth1" } futures = { workspace = true }
futures = "0.3.7" genesis = { workspace = true }
genesis = { path = "../genesis" } int_to_bytes = { workspace = true }
int_to_bytes = { path = "../../consensus/int_to_bytes" } rand = { workspace = true }
rand = "0.8.5" proto_array = { workspace = true }
proto_array = { path = "../../consensus/proto_array" } lru = { workspace = true }
lru = "0.7.1" tempfile = { workspace = true }
tempfile = "3.1.0" bitvec = { workspace = true }
bitvec = "0.20.4" bls = { workspace = true }
bls = { path = "../../crypto/bls" } kzg = { workspace = true }
kzg = { path = "../../crypto/kzg" } safe_arith = { workspace = true }
safe_arith = { path = "../../consensus/safe_arith" } fork_choice = { workspace = true }
fork_choice = { path = "../../consensus/fork_choice" } task_executor = { workspace = true }
task_executor = { path = "../../common/task_executor" } derivative = { workspace = true }
derivative = "2.1.1" itertools = { workspace = true }
itertools = "0.10.0" slasher = { workspace = true }
slasher = { path = "../../slasher" } eth2 = { workspace = true }
eth2 = { path = "../../common/eth2" } strum = { workspace = true }
strum = { version = "0.24.0", features = ["derive"] } logging = { workspace = true }
logging = { path = "../../common/logging" } execution_layer = { workspace = true }
execution_layer = { path = "../execution_layer" } sensitive_url = { workspace = true }
sensitive_url = { path = "../../common/sensitive_url" } superstruct = { workspace = true }
superstruct = "0.5.0" hex = { workspace = true }
hex = "0.4.2" exit-future = { workspace = true }
exit-future = "0.2.0" oneshot_broadcast = { path = "../../common/oneshot_broadcast/" }
unused_port = {path = "../../common/unused_port"} slog-term = { workspace = true }
oneshot_broadcast = { path = "../../common/oneshot_broadcast" } slog-async = { workspace = true }
slog-term = "2.6.0"
slog-async = "2.5.0"
[[test]] [[test]]
name = "beacon_chain_tests" name = "beacon_chain_tests"

View File

@ -874,10 +874,10 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
/// ///
/// May return a database error. /// May return a database error.
pub fn state_root_at_slot(&self, request_slot: Slot) -> Result<Option<Hash256>, Error> { pub fn state_root_at_slot(&self, request_slot: Slot) -> Result<Option<Hash256>, Error> {
if request_slot > self.slot()? { if request_slot == self.spec.genesis_slot {
return Ok(None);
} else if request_slot == self.spec.genesis_slot {
return Ok(Some(self.genesis_state_root)); return Ok(Some(self.genesis_state_root));
} else if request_slot > self.slot()? {
return Ok(None);
} }
// Check limits w.r.t historic state bounds. // Check limits w.r.t historic state bounds.
@ -954,10 +954,10 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
/// ///
/// May return a database error. /// May return a database error.
fn block_root_at_slot_skips_none(&self, request_slot: Slot) -> Result<Option<Hash256>, Error> { fn block_root_at_slot_skips_none(&self, request_slot: Slot) -> Result<Option<Hash256>, Error> {
if request_slot > self.slot()? { if request_slot == self.spec.genesis_slot {
return Ok(None);
} else if request_slot == self.spec.genesis_slot {
return Ok(Some(self.genesis_block_root)); return Ok(Some(self.genesis_block_root));
} else if request_slot > self.slot()? {
return Ok(None);
} }
let prev_slot = request_slot.saturating_sub(1_u64); let prev_slot = request_slot.saturating_sub(1_u64);
@ -1017,10 +1017,10 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
/// ///
/// May return a database error. /// May return a database error.
fn block_root_at_slot_skips_prev(&self, request_slot: Slot) -> Result<Option<Hash256>, Error> { fn block_root_at_slot_skips_prev(&self, request_slot: Slot) -> Result<Option<Hash256>, Error> {
if request_slot > self.slot()? { if request_slot == self.spec.genesis_slot {
return Ok(None);
} else if request_slot == self.spec.genesis_slot {
return Ok(Some(self.genesis_block_root)); return Ok(Some(self.genesis_block_root));
} else if request_slot > self.slot()? {
return Ok(None);
} }
// Try an optimized path of reading the root directly from the head state. // Try an optimized path of reading the root directly from the head state.

View File

@ -1,4 +1,4 @@
use serde_derive::Serialize; use serde::Serialize;
use std::sync::Arc; use std::sync::Arc;
use types::{ use types::{
beacon_state::CloneConfig, AbstractExecPayload, BeaconState, EthSpec, FullPayload, Hash256, beacon_state::CloneConfig, AbstractExecPayload, BeaconState, EthSpec, FullPayload, Hash256,

View File

@ -811,11 +811,8 @@ impl<T: BeaconChainTypes> GossipVerifiedBlock<T> {
// reboot if the `observed_block_producers` cache is empty. In that case, without this // reboot if the `observed_block_producers` cache is empty. In that case, without this
// check, we will load the parent and state from disk only to find out later that we // check, we will load the parent and state from disk only to find out later that we
// already know this block. // already know this block.
if chain let fork_choice_read_lock = chain.canonical_head.fork_choice_read_lock();
.canonical_head if fork_choice_read_lock.contains_block(&block_root) {
.fork_choice_read_lock()
.contains_block(&block_root)
{
return Err(BlockError::BlockIsAlreadyKnown); return Err(BlockError::BlockIsAlreadyKnown);
} }
@ -824,9 +821,10 @@ impl<T: BeaconChainTypes> GossipVerifiedBlock<T> {
// We check this *before* we load the parent so that we can return a more detailed error. // We check this *before* we load the parent so that we can return a more detailed error.
let block = check_block_is_finalized_checkpoint_or_descendant( let block = check_block_is_finalized_checkpoint_or_descendant(
chain, chain,
&chain.canonical_head.fork_choice_read_lock(), &fork_choice_read_lock,
block, block,
)?; )?;
drop(fork_choice_read_lock);
let block_epoch = block.slot().epoch(T::EthSpec::slots_per_epoch()); let block_epoch = block.slot().epoch(T::EthSpec::slots_per_epoch());
let (parent_block, block) = verify_parent_block_is_known(chain, block)?; let (parent_block, block) = verify_parent_block_is_known(chain, block)?;

View File

@ -1,5 +1,5 @@
pub use proto_array::{DisallowedReOrgOffsets, ReOrgThreshold}; pub use proto_array::{DisallowedReOrgOffsets, ReOrgThreshold};
use serde_derive::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::time::Duration; use std::time::Duration;
use types::{Checkpoint, Epoch, ProgressiveBalancesMode}; use types::{Checkpoint, Epoch, ProgressiveBalancesMode};

View File

@ -149,6 +149,8 @@ pub enum BeaconChainError {
BlockVariantLacksExecutionPayload(Hash256), BlockVariantLacksExecutionPayload(Hash256),
ExecutionLayerErrorPayloadReconstruction(ExecutionBlockHash, Box<execution_layer::Error>), ExecutionLayerErrorPayloadReconstruction(ExecutionBlockHash, Box<execution_layer::Error>),
EngineGetCapabilititesFailed(Box<execution_layer::Error>), EngineGetCapabilititesFailed(Box<execution_layer::Error>),
ExecutionLayerGetBlockByNumberFailed(Box<execution_layer::Error>),
ExecutionLayerGetBlockByHashFailed(Box<execution_layer::Error>),
BlockHashMissingFromExecutionLayer(ExecutionBlockHash), BlockHashMissingFromExecutionLayer(ExecutionBlockHash),
InconsistentPayloadReconstructed { InconsistentPayloadReconstructed {
slot: Slot, slot: Slot,

View File

@ -1,8 +1,10 @@
//! Provides tools for checking if a node is ready for the Bellatrix upgrade and following merge //! Provides tools for checking if a node is ready for the Bellatrix upgrade and following merge
//! transition. //! transition.
use crate::{BeaconChain, BeaconChainTypes}; use crate::{BeaconChain, BeaconChainError as Error, BeaconChainTypes};
use execution_layer::BlockByNumberQuery;
use serde::{Deserialize, Serialize, Serializer}; use serde::{Deserialize, Serialize, Serializer};
use slog::debug;
use std::fmt; use std::fmt;
use std::fmt::Write; use std::fmt::Write;
use types::*; use types::*;
@ -120,6 +122,25 @@ impl fmt::Display for MergeReadiness {
} }
} }
pub enum GenesisExecutionPayloadStatus {
Correct(ExecutionBlockHash),
BlockHashMismatch {
got: ExecutionBlockHash,
expected: ExecutionBlockHash,
},
TransactionsRootMismatch {
got: Hash256,
expected: Hash256,
},
WithdrawalsRootMismatch {
got: Hash256,
expected: Hash256,
},
OtherMismatch,
Irrelevant,
AlreadyHappened,
}
impl<T: BeaconChainTypes> BeaconChain<T> { impl<T: BeaconChainTypes> BeaconChain<T> {
/// Returns `true` if user has an EL configured, or if the Bellatrix fork has occurred or will /// Returns `true` if user has an EL configured, or if the Bellatrix fork has occurred or will
/// occur within `MERGE_READINESS_PREPARATION_SECONDS`. /// occur within `MERGE_READINESS_PREPARATION_SECONDS`.
@ -144,9 +165,9 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
} }
/// Attempts to connect to the EL and confirm that it is ready for the merge. /// Attempts to connect to the EL and confirm that it is ready for the merge.
pub async fn check_merge_readiness(&self) -> MergeReadiness { pub async fn check_merge_readiness(&self, current_slot: Slot) -> MergeReadiness {
if let Some(el) = self.execution_layer.as_ref() { if let Some(el) = self.execution_layer.as_ref() {
if !el.is_synced_for_notifier().await { if !el.is_synced_for_notifier(current_slot).await {
// The EL is not synced. // The EL is not synced.
return MergeReadiness::NotSynced; return MergeReadiness::NotSynced;
} }
@ -161,6 +182,91 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
MergeReadiness::NoExecutionEndpoint MergeReadiness::NoExecutionEndpoint
} }
} }
/// Check that the execution payload embedded in the genesis state matches the EL's genesis
/// block.
pub async fn check_genesis_execution_payload_is_correct(
&self,
) -> Result<GenesisExecutionPayloadStatus, Error> {
let head_snapshot = self.head_snapshot();
let genesis_state = &head_snapshot.beacon_state;
if genesis_state.slot() != 0 {
return Ok(GenesisExecutionPayloadStatus::AlreadyHappened);
}
let Ok(latest_execution_payload_header) = genesis_state.latest_execution_payload_header()
else {
return Ok(GenesisExecutionPayloadStatus::Irrelevant);
};
let fork = self.spec.fork_name_at_epoch(Epoch::new(0));
let execution_layer = self
.execution_layer
.as_ref()
.ok_or(Error::ExecutionLayerMissing)?;
let exec_block_hash = latest_execution_payload_header.block_hash();
// Use getBlockByNumber(0) to check that the block hash matches.
// At present, Geth does not respond to engine_getPayloadBodiesByRange before genesis.
let execution_block = execution_layer
.get_block_by_number(BlockByNumberQuery::Tag("0x0"))
.await
.map_err(|e| Error::ExecutionLayerGetBlockByNumberFailed(Box::new(e)))?
.ok_or(Error::BlockHashMissingFromExecutionLayer(exec_block_hash))?;
if execution_block.block_hash != exec_block_hash {
return Ok(GenesisExecutionPayloadStatus::BlockHashMismatch {
got: execution_block.block_hash,
expected: exec_block_hash,
});
}
// Double-check the block by reconstructing it.
let execution_payload = execution_layer
.get_payload_by_hash_legacy(exec_block_hash, fork)
.await
.map_err(|e| Error::ExecutionLayerGetBlockByHashFailed(Box::new(e)))?
.ok_or(Error::BlockHashMissingFromExecutionLayer(exec_block_hash))?;
// Verify payload integrity.
let header_from_payload = ExecutionPayloadHeader::from(execution_payload.to_ref());
let got_transactions_root = header_from_payload.transactions_root();
let expected_transactions_root = latest_execution_payload_header.transactions_root();
let got_withdrawals_root = header_from_payload.withdrawals_root().ok();
let expected_withdrawals_root = latest_execution_payload_header.withdrawals_root().ok();
if got_transactions_root != expected_transactions_root {
return Ok(GenesisExecutionPayloadStatus::TransactionsRootMismatch {
got: got_transactions_root,
expected: expected_transactions_root,
});
}
if let Some(&expected) = expected_withdrawals_root {
if let Some(&got) = got_withdrawals_root {
if got != expected {
return Ok(GenesisExecutionPayloadStatus::WithdrawalsRootMismatch {
got,
expected,
});
}
}
}
if header_from_payload.to_ref() != latest_execution_payload_header {
debug!(
self.log,
"Genesis execution payload reconstruction failure";
"consensus_node_header" => ?latest_execution_payload_header,
"execution_node_header" => ?header_from_payload
);
return Ok(GenesisExecutionPayloadStatus::OtherMismatch);
}
Ok(GenesisExecutionPayloadStatus::Correct(exec_block_hash))
}
} }
/// Utility function to serialize a Uint256 as a decimal string. /// Utility function to serialize a Uint256 as a decimal string.

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, MockExecutionLayer, TestingBuilder, DEFAULT_JWT_SECRET, ExecutionBlockGenerator, MockBuilder, MockBuilderServer, MockExecutionLayer,
DEFAULT_TERMINAL_BLOCK, DEFAULT_JWT_SECRET, DEFAULT_TERMINAL_BLOCK,
}, },
ExecutionLayer, ExecutionLayer,
}; };
@ -175,7 +175,6 @@ pub struct Builder<T: BeaconChainTypes> {
store_mutator: Option<BoxedMutator<T::EthSpec, T::HotStore, T::ColdStore>>, store_mutator: Option<BoxedMutator<T::EthSpec, T::HotStore, T::ColdStore>>,
execution_layer: Option<ExecutionLayer<T::EthSpec>>, execution_layer: Option<ExecutionLayer<T::EthSpec>>,
mock_execution_layer: Option<MockExecutionLayer<T::EthSpec>>, mock_execution_layer: Option<MockExecutionLayer<T::EthSpec>>,
mock_builder: Option<TestingBuilder<T::EthSpec>>,
testing_slot_clock: Option<TestingSlotClock>, testing_slot_clock: Option<TestingSlotClock>,
runtime: TestRuntime, runtime: TestRuntime,
log: Logger, log: Logger,
@ -311,7 +310,6 @@ where
store_mutator: None, store_mutator: None,
execution_layer: None, execution_layer: None,
mock_execution_layer: None, mock_execution_layer: None,
mock_builder: None,
testing_slot_clock: None, testing_slot_clock: None,
runtime, runtime,
log, log,
@ -451,50 +449,21 @@ where
self self
} }
pub fn mock_execution_layer(mut self) -> Self { pub fn mock_execution_layer(self) -> Self {
self.mock_execution_layer_with_config(None)
}
pub fn mock_execution_layer_with_config(mut self, builder_threshold: Option<u128>) -> Self {
let mock = mock_execution_layer_from_parts::<E>( let mock = mock_execution_layer_from_parts::<E>(
self.spec.as_ref().expect("cannot build without spec"), self.spec.as_ref().expect("cannot build without spec"),
self.runtime.task_executor.clone(), self.runtime.task_executor.clone(),
None, builder_threshold,
None,
); );
self.execution_layer = Some(mock.el.clone()); self.execution_layer = Some(mock.el.clone());
self.mock_execution_layer = Some(mock); self.mock_execution_layer = Some(mock);
self self
} }
pub fn mock_execution_layer_with_builder(
mut self,
beacon_url: SensitiveUrl,
builder_threshold: Option<u128>,
) -> Self {
// Get a random unused port
let port = unused_port::unused_tcp4_port().unwrap();
let builder_url = SensitiveUrl::parse(format!("http://127.0.0.1:{port}").as_str()).unwrap();
let spec = self.spec.as_ref().expect("cannot build without spec");
let mock_el = mock_execution_layer_from_parts::<E>(
spec,
self.runtime.task_executor.clone(),
Some(builder_url.clone()),
builder_threshold,
)
.move_to_terminal_block();
let mock_el_url = SensitiveUrl::parse(mock_el.server.url().as_str()).unwrap();
self.mock_builder = Some(TestingBuilder::new(
mock_el_url,
builder_url,
beacon_url,
spec.clone(),
self.runtime.task_executor.clone(),
));
self.execution_layer = Some(mock_el.el.clone());
self.mock_execution_layer = Some(mock_el);
self
}
/// Instruct the mock execution engine to always return a "valid" response to any payload it is /// Instruct the mock execution engine to always return a "valid" response to any payload it is
/// asked to execute. /// asked to execute.
pub fn mock_execution_layer_all_payloads_valid(self) -> Self { pub fn mock_execution_layer_all_payloads_valid(self) -> Self {
@ -581,7 +550,7 @@ where
shutdown_receiver: Arc::new(Mutex::new(shutdown_receiver)), shutdown_receiver: Arc::new(Mutex::new(shutdown_receiver)),
runtime: self.runtime, runtime: self.runtime,
mock_execution_layer: self.mock_execution_layer, mock_execution_layer: self.mock_execution_layer,
mock_builder: self.mock_builder.map(Arc::new), mock_builder: None,
blob_signature_cache: <_>::default(), blob_signature_cache: <_>::default(),
rng: make_rng(), rng: make_rng(),
} }
@ -591,7 +560,6 @@ where
pub fn mock_execution_layer_from_parts<T: EthSpec>( pub fn mock_execution_layer_from_parts<T: EthSpec>(
spec: &ChainSpec, spec: &ChainSpec,
task_executor: TaskExecutor, task_executor: TaskExecutor,
builder_url: Option<SensitiveUrl>,
builder_threshold: Option<u128>, builder_threshold: Option<u128>,
) -> MockExecutionLayer<T> { ) -> MockExecutionLayer<T> {
let shanghai_time = spec.capella_fork_epoch.map(|epoch| { let shanghai_time = spec.capella_fork_epoch.map(|epoch| {
@ -615,7 +583,6 @@ pub fn mock_execution_layer_from_parts<T: EthSpec>(
builder_threshold, builder_threshold,
Some(JwtKey::from_slice(&DEFAULT_JWT_SECRET).unwrap()), Some(JwtKey::from_slice(&DEFAULT_JWT_SECRET).unwrap()),
spec.clone(), spec.clone(),
builder_url,
Some(kzg), Some(kzg),
) )
} }
@ -639,7 +606,7 @@ pub struct BeaconChainHarness<T: BeaconChainTypes> {
pub runtime: TestRuntime, pub runtime: TestRuntime,
pub mock_execution_layer: Option<MockExecutionLayer<T::EthSpec>>, pub mock_execution_layer: Option<MockExecutionLayer<T::EthSpec>>,
pub mock_builder: Option<Arc<TestingBuilder<T::EthSpec>>>, pub mock_builder: Option<Arc<MockBuilder<T::EthSpec>>>,
/// Cache for blob signature because we don't need them for import, but we do need them /// Cache for blob signature because we don't need them for import, but we do need them
/// to test gossip validation. We always make them during block production but drop them /// to test gossip validation. We always make them during block production but drop them
@ -695,6 +662,49 @@ where
.execution_block_generator() .execution_block_generator()
} }
pub fn set_mock_builder(&mut self, beacon_url: SensitiveUrl) -> MockBuilderServer {
let mock_el = self
.mock_execution_layer
.as_ref()
.expect("harness was not built with mock execution layer");
let mock_el_url = SensitiveUrl::parse(mock_el.server.url().as_str()).unwrap();
// Create the builder, listening on a free port.
let (mock_builder, mock_builder_server) = MockBuilder::new_for_testing(
mock_el_url,
beacon_url,
self.spec.clone(),
self.runtime.task_executor.clone(),
);
// 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 = builder_listen_addr.port();
mock_el
.el
.set_builder_url(
SensitiveUrl::parse(format!("http://127.0.0.1:{port}").as_str()).unwrap(),
None,
)
.unwrap();
self.mock_builder = Some(Arc::new(mock_builder));
// Sanity check.
let el_builder = self
.chain
.execution_layer
.as_ref()
.unwrap()
.builder()
.unwrap();
let mock_el_builder = mock_el.el.builder().unwrap();
assert!(Arc::ptr_eq(&el_builder, &mock_el_builder));
mock_builder_server
}
pub fn get_head_block(&self) -> RpcBlock<E> { pub fn get_head_block(&self) -> RpcBlock<E> {
let block = self.chain.head_beacon_block(); let block = self.chain.head_beacon_block();
let block_root = block.canonical_root(); let block_root = block.canonical_root();

View File

@ -2159,12 +2159,8 @@ async fn weak_subjectivity_sync_test(slots: Vec<Slot>, checkpoint_slot: Slot) {
.map_err(|e| println!("Unable to read trusted setup file: {}", e)) .map_err(|e| println!("Unable to read trusted setup file: {}", e))
.unwrap(); .unwrap();
let mock = mock_execution_layer_from_parts( let mock =
&harness.spec, mock_execution_layer_from_parts(&harness.spec, harness.runtime.task_executor.clone(), None);
harness.runtime.task_executor.clone(),
None,
None,
);
// Initialise a new beacon chain from the finalized checkpoint. // Initialise a new beacon chain from the finalized checkpoint.
// The slot clock must be set to a time ahead of the checkpoint state. // The slot clock must be set to a time ahead of the checkpoint state.

View File

@ -1,26 +1,26 @@
[package] [package]
name = "beacon_processor" name = "beacon_processor"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = { workspace = true }
[dependencies] [dependencies]
slog = { version = "2.5.2", features = ["max_level_trace"] } slog = { workspace = true }
itertools = "0.10.0" itertools = { workspace = true }
logging = { path = "../../common/logging" } logging = { workspace = true }
tokio = { version = "1.14.0", features = ["full"] } tokio = { workspace = true }
tokio-util = { version = "0.6.3", features = ["time"] } tokio-util = { workspace = true }
futures = "0.3.7" futures = { workspace = true }
fnv = "1.0.7" fnv = { workspace = true }
strum = "0.24.0" strum = { workspace = true }
task_executor = { path = "../../common/task_executor" } task_executor = { workspace = true }
slot_clock = { path = "../../common/slot_clock" } slot_clock = { workspace = true }
lighthouse_network = { path = "../lighthouse_network" } lighthouse_network = { workspace = true }
hex = "0.4.2" hex = { workspace = true }
derivative = "2.2.0" derivative = { workspace = true }
types = { path = "../../consensus/types" } types = { workspace = true }
ethereum_ssz = "0.5.0" ethereum_ssz = { workspace = true }
lazy_static = "1.4.0" lazy_static = { workspace = true }
lighthouse_metrics = { path = "../../common/lighthouse_metrics" } lighthouse_metrics = { workspace = true }
parking_lot = "0.12.0" parking_lot = { workspace = true }
num_cpus = "1.13.0" num_cpus = { workspace = true }
serde = { version = "1.0.116", features = ["derive"] } serde = { workspace = true }

View File

@ -1,13 +1,13 @@
[package] [package]
name = "builder_client" name = "builder_client"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = { workspace = true }
authors = ["Sean Anderson <sean@sigmaprime.io>"] authors = ["Sean Anderson <sean@sigmaprime.io>"]
[dependencies] [dependencies]
reqwest = { version = "0.11.0", features = ["json","stream"] } reqwest = { workspace = true }
sensitive_url = { path = "../../common/sensitive_url" } sensitive_url = { workspace = true }
eth2 = { path = "../../common/eth2" } eth2 = { workspace = true }
serde = { version = "1.0.116", features = ["derive"] } serde = { workspace = true }
serde_json = "1.0.58" serde_json = { workspace = true }
lighthouse_version = { path = "../../common/lighthouse_version" } lighthouse_version = { workspace = true }

View File

@ -2,45 +2,46 @@
name = "client" name = "client"
version = "0.2.0" version = "0.2.0"
authors = ["Sigma Prime <contact@sigmaprime.io>"] authors = ["Sigma Prime <contact@sigmaprime.io>"]
edition = "2021" edition = { workspace = true }
[dev-dependencies] [dev-dependencies]
serde_yaml = "0.8.13" serde_yaml = { workspace = true }
operation_pool = { path = "../operation_pool" } state_processing = { workspace = true }
tokio = "1.14.0" operation_pool = { workspace = true }
tokio = { workspace = true }
[dependencies] [dependencies]
state_processing = { path = "../../consensus/state_processing" } beacon_chain = { workspace = true }
beacon_chain = { path = "../beacon_chain" } store = { workspace = true }
store = { path = "../store" } network = { workspace = true }
network = { path = "../network" }
timer = { path = "../timer" } timer = { path = "../timer" }
lighthouse_network = { path = "../lighthouse_network" } lighthouse_network = { workspace = true }
logging = { path = "../../common/logging" } logging = { workspace = true }
parking_lot = "0.12.0" parking_lot = { workspace = true }
types = { path = "../../consensus/types" } types = { workspace = true }
eth2_config = { path = "../../common/eth2_config" } eth2_config = { workspace = true }
slot_clock = { path = "../../common/slot_clock" } slot_clock = { workspace = true }
serde = "1.0.116" serde = { workspace = true }
serde_derive = "1.0.116" serde_derive = "1.0.116"
error-chain = "0.12.4" error-chain = { workspace = true }
slog = { version = "2.5.2", features = ["max_level_trace"] } slog = { workspace = true }
tokio = "1.14.0" tokio = { workspace = true }
dirs = "3.0.1" dirs = { workspace = true }
eth1 = { path = "../eth1" } eth1 = { workspace = true }
eth2 = { path = "../../common/eth2" } eth2 = { workspace = true }
sensitive_url = { path = "../../common/sensitive_url" } sensitive_url = { workspace = true }
genesis = { path = "../genesis" } genesis = { workspace = true }
task_executor = { path = "../../common/task_executor" } task_executor = { workspace = true }
environment = { path = "../../lighthouse/environment" } environment = { workspace = true }
lazy_static = "1.4.0" lazy_static = { workspace = true }
lighthouse_metrics = { path = "../../common/lighthouse_metrics" } lighthouse_metrics = { workspace = true }
time = "0.3.5" time = "0.3.5"
directory = {path = "../../common/directory"} directory = { workspace = true }
http_api = { path = "../http_api" } http_api = { workspace = true }
http_metrics = { path = "../http_metrics" } http_metrics = { path = "../http_metrics" }
slasher = { path = "../../slasher" } slasher = { workspace = true }
slasher_service = { path = "../../slasher/service" } slasher_service = { path = "../../slasher/service" }
monitoring_api = {path = "../../common/monitoring_api"} monitoring_api = { workspace = true }
execution_layer = { path = "../execution_layer" } execution_layer = { workspace = true }
beacon_processor = { path = "../beacon_processor" } beacon_processor = { workspace = true }
num_cpus = { workspace = true }

View File

@ -259,7 +259,7 @@ where
"Starting from known genesis state"; "Starting from known genesis state";
); );
let genesis_state = genesis_state(&runtime_context, &config, log)?; let genesis_state = genesis_state(&runtime_context, &config, log).await?;
builder.genesis_state(genesis_state).map(|v| (v, None))? builder.genesis_state(genesis_state).map(|v| (v, None))?
} }
@ -279,7 +279,7 @@ where
.map_err(|e| format!("Unable to parse weak subj state SSZ: {:?}", e))?; .map_err(|e| format!("Unable to parse weak subj state SSZ: {:?}", e))?;
let anchor_block = SignedBeaconBlock::from_ssz_bytes(&anchor_block_bytes, &spec) let anchor_block = SignedBeaconBlock::from_ssz_bytes(&anchor_block_bytes, &spec)
.map_err(|e| format!("Unable to parse weak subj block SSZ: {:?}", e))?; .map_err(|e| format!("Unable to parse weak subj block SSZ: {:?}", e))?;
let genesis_state = genesis_state(&runtime_context, &config, log)?; let genesis_state = genesis_state(&runtime_context, &config, log).await?;
builder builder
.weak_subjectivity_state(anchor_state, anchor_block, genesis_state) .weak_subjectivity_state(anchor_state, anchor_block, genesis_state)
@ -380,7 +380,7 @@ where
debug!(context.log(), "Downloaded finalized block"); debug!(context.log(), "Downloaded finalized block");
let genesis_state = genesis_state(&runtime_context, &config, log)?; let genesis_state = genesis_state(&runtime_context, &config, log).await?;
info!( info!(
context.log(), context.log(),
@ -1099,7 +1099,7 @@ where
} }
/// Obtain the genesis state from the `eth2_network_config` in `context`. /// Obtain the genesis state from the `eth2_network_config` in `context`.
fn genesis_state<T: EthSpec>( async fn genesis_state<T: EthSpec>(
context: &RuntimeContext<T>, context: &RuntimeContext<T>,
config: &ClientConfig, config: &ClientConfig,
log: &Logger, log: &Logger,
@ -1113,6 +1113,7 @@ fn genesis_state<T: EthSpec>(
config.genesis_state_url.as_deref(), config.genesis_state_url.as_deref(),
config.genesis_state_url_timeout, config.genesis_state_url_timeout,
log, log,
)? )
.await?
.ok_or_else(|| "Genesis state is unknown".to_string()) .ok_or_else(|| "Genesis state is unknown".to_string())
} }

View File

@ -46,20 +46,6 @@ impl<T: BeaconChainTypes> Client<T> {
self.http_metrics_listen_addr self.http_metrics_listen_addr
} }
/// Returns the ipv4 port of the client's libp2p stack, if it was started.
pub fn libp2p_listen_ipv4_port(&self) -> Option<u16> {
self.network_globals
.as_ref()
.and_then(|n| n.listen_port_tcp4())
}
/// Returns the ipv6 port of the client's libp2p stack, if it was started.
pub fn libp2p_listen_ipv6_port(&self) -> Option<u16> {
self.network_globals
.as_ref()
.and_then(|n| n.listen_port_tcp6())
}
/// Returns the list of libp2p addresses the client is listening to. /// Returns the list of libp2p addresses the client is listening to.
pub fn libp2p_listen_addresses(&self) -> Option<Vec<Multiaddr>> { pub fn libp2p_listen_addresses(&self) -> Option<Vec<Multiaddr>> {
self.network_globals.as_ref().map(|n| n.listen_multiaddrs()) self.network_globals.as_ref().map(|n| n.listen_multiaddrs())

View File

@ -1,7 +1,7 @@
use crate::metrics; use crate::metrics;
use beacon_chain::{ use beacon_chain::{
capella_readiness::CapellaReadiness, capella_readiness::CapellaReadiness,
merge_readiness::{MergeConfig, MergeReadiness}, merge_readiness::{GenesisExecutionPayloadStatus, MergeConfig, MergeReadiness},
BeaconChain, BeaconChainTypes, ExecutionStatus, BeaconChain, BeaconChainTypes, ExecutionStatus,
}; };
use lighthouse_network::{types::SyncState, NetworkGlobals}; use lighthouse_network::{types::SyncState, NetworkGlobals};
@ -62,6 +62,9 @@ pub fn spawn_notifier<T: BeaconChainTypes>(
"wait_time" => estimated_time_pretty(Some(next_slot.as_secs() as f64)), "wait_time" => estimated_time_pretty(Some(next_slot.as_secs() as f64)),
); );
eth1_logging(&beacon_chain, &log); eth1_logging(&beacon_chain, &log);
merge_readiness_logging(Slot::new(0), &beacon_chain, &log).await;
capella_readiness_logging(Slot::new(0), &beacon_chain, &log).await;
genesis_execution_payload_logging(&beacon_chain, &log).await;
sleep(slot_duration).await; sleep(slot_duration).await;
} }
_ => break, _ => break,
@ -365,7 +368,7 @@ async fn merge_readiness_logging<T: BeaconChainTypes>(
return; return;
} }
match beacon_chain.check_merge_readiness().await { match beacon_chain.check_merge_readiness(current_slot).await {
MergeReadiness::Ready { MergeReadiness::Ready {
config, config,
current_difficulty, current_difficulty,
@ -476,6 +479,79 @@ async fn capella_readiness_logging<T: BeaconChainTypes>(
} }
} }
async fn genesis_execution_payload_logging<T: BeaconChainTypes>(
beacon_chain: &BeaconChain<T>,
log: &Logger,
) {
match beacon_chain
.check_genesis_execution_payload_is_correct()
.await
{
Ok(GenesisExecutionPayloadStatus::Correct(block_hash)) => {
info!(
log,
"Execution enabled from genesis";
"genesis_payload_block_hash" => ?block_hash,
);
}
Ok(GenesisExecutionPayloadStatus::BlockHashMismatch { got, expected }) => {
error!(
log,
"Genesis payload block hash mismatch";
"info" => "genesis is misconfigured and likely to fail",
"consensus_node_block_hash" => ?expected,
"execution_node_block_hash" => ?got,
);
}
Ok(GenesisExecutionPayloadStatus::TransactionsRootMismatch { got, expected }) => {
error!(
log,
"Genesis payload transactions root mismatch";
"info" => "genesis is misconfigured and likely to fail",
"consensus_node_transactions_root" => ?expected,
"execution_node_transactions_root" => ?got,
);
}
Ok(GenesisExecutionPayloadStatus::WithdrawalsRootMismatch { got, expected }) => {
error!(
log,
"Genesis payload withdrawals root mismatch";
"info" => "genesis is misconfigured and likely to fail",
"consensus_node_withdrawals_root" => ?expected,
"execution_node_withdrawals_root" => ?got,
);
}
Ok(GenesisExecutionPayloadStatus::OtherMismatch) => {
error!(
log,
"Genesis payload header mismatch";
"info" => "genesis is misconfigured and likely to fail",
"detail" => "see debug logs for payload headers"
);
}
Ok(GenesisExecutionPayloadStatus::Irrelevant) => {
info!(
log,
"Execution is not enabled from genesis";
);
}
Ok(GenesisExecutionPayloadStatus::AlreadyHappened) => {
warn!(
log,
"Unable to check genesis which has already occurred";
"info" => "this is probably a race condition or a bug"
);
}
Err(e) => {
error!(
log,
"Unable to check genesis execution payload";
"error" => ?e
);
}
}
}
fn eth1_logging<T: BeaconChainTypes>(beacon_chain: &BeaconChain<T>, log: &Logger) { fn eth1_logging<T: BeaconChainTypes>(beacon_chain: &BeaconChain<T>, log: &Logger) {
let current_slot_opt = beacon_chain.slot().ok(); let current_slot_opt = beacon_chain.slot().ok();

View File

@ -2,33 +2,33 @@
name = "eth1" name = "eth1"
version = "0.2.0" version = "0.2.0"
authors = ["Paul Hauner <paul@paulhauner.com>"] authors = ["Paul Hauner <paul@paulhauner.com>"]
edition = "2021" edition = { workspace = true }
[dev-dependencies] [dev-dependencies]
eth1_test_rig = { path = "../../testing/eth1_test_rig" } eth1_test_rig = { workspace = true }
serde_yaml = "0.8.13" serde_yaml = { workspace = true }
sloggers = { version = "2.1.1", features = ["json"] } sloggers = { workspace = true }
environment = { path = "../../lighthouse/environment" } environment = { workspace = true }
[dependencies] [dependencies]
reqwest = { version = "0.11.0", features = ["native-tls-vendored"] } reqwest = { workspace = true }
execution_layer = { path = "../execution_layer" } execution_layer = { workspace = true }
futures = "0.3.7" futures = { workspace = true }
serde_json = "1.0.58" serde_json = { workspace = true }
serde = { version = "1.0.116", features = ["derive"] } serde = { workspace = true }
hex = "0.4.2" hex = { workspace = true }
types = { path = "../../consensus/types"} types = { workspace = true }
merkle_proof = { path = "../../consensus/merkle_proof"} merkle_proof = { workspace = true }
ethereum_ssz = "0.5.0" ethereum_ssz = { workspace = true }
ethereum_ssz_derive = "0.5.3" ethereum_ssz_derive = { workspace = true }
tree_hash = "0.5.2" tree_hash = { workspace = true }
parking_lot = "0.12.0" parking_lot = { workspace = true }
slog = "2.5.2" slog = { workspace = true }
superstruct = "0.5.0" superstruct = { workspace = true }
tokio = { version = "1.14.0", features = ["full"] } tokio = { workspace = true }
state_processing = { path = "../../consensus/state_processing" } state_processing = { workspace = true }
lighthouse_metrics = { path = "../../common/lighthouse_metrics"} lighthouse_metrics = { workspace = true }
lazy_static = "1.4.0" lazy_static = { workspace = true }
task_executor = { path = "../../common/task_executor" } task_executor = { workspace = true }
eth2 = { path = "../../common/eth2" } eth2 = { workspace = true }
sensitive_url = { path = "../../common/sensitive_url" } sensitive_url = { workspace = true }

View File

@ -1,55 +1,58 @@
[package] [package]
name = "execution_layer" name = "execution_layer"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = { workspace = true }
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
types = { path = "../../consensus/types"} types = { workspace = true }
tokio = { version = "1.10.0", features = ["full"] } tokio = { workspace = true }
async-trait = "0.1.51" async-trait = "0.1.51"
slog = "2.5.2" slog = { workspace = true }
futures = "0.3.7" futures = { workspace = true }
sensitive_url = { path = "../../common/sensitive_url" } sensitive_url = { workspace = true }
reqwest = { version = "0.11.0", features = ["json","stream"] } reqwest = { workspace = true }
ethereum_serde_utils = "0.5.0" ethereum_serde_utils = { workspace = true }
serde_json = "1.0.58" serde_json = { workspace = true }
serde = { version = "1.0.116", features = ["derive"] } serde = { workspace = true }
warp = { version = "0.3.2", features = ["tls"] } warp = { workspace = true }
jsonwebtoken = "8" jsonwebtoken = "8"
environment = { path = "../../lighthouse/environment" } environment = { workspace = true }
bytes = "1.1.0" bytes = { workspace = true }
task_executor = { path = "../../common/task_executor" } task_executor = { workspace = true }
hex = "0.4.2" hex = { workspace = true }
ethereum_ssz = "0.5.0" ethereum_ssz = { workspace = true }
ssz_types = "0.5.4" ssz_types = { workspace = true }
eth2 = { path = "../../common/eth2" } eth2 = { workspace = true }
kzg = { path = "../../crypto/kzg" } kzg = { workspace = true }
state_processing = { path = "../../consensus/state_processing" } state_processing = { workspace = true }
superstruct = "0.6.0" superstruct = { workspace = true }
lru = "0.7.1" lru = { workspace = true }
exit-future = "0.2.0" exit-future = { workspace = true }
tree_hash = "0.5.2" tree_hash = { workspace = true }
tree_hash_derive = "0.5.0" tree_hash_derive = { workspace = true }
parking_lot = "0.12.0" parking_lot = { workspace = true }
slot_clock = { path = "../../common/slot_clock" } slot_clock = { workspace = true }
tempfile = "3.1.0" tempfile = { workspace = true }
rand = "0.8.5" rand = { workspace = true }
zeroize = { version = "1.4.2", features = ["zeroize_derive"] } zeroize = { workspace = true }
lighthouse_metrics = { path = "../../common/lighthouse_metrics" } lighthouse_metrics = { workspace = true }
lazy_static = "1.4.0" lazy_static = { workspace = true }
ethers-core = "1.0.2" ethers-core = { workspace = true }
builder_client = { path = "../builder_client" } builder_client = { path = "../builder_client" }
fork_choice = { path = "../../consensus/fork_choice" } fork_choice = { workspace = true }
#PR: https://github.com/ralexstokes/mev-rs/pull/124 #PR: https://github.com/ralexstokes/mev-rs/pull/124
mev-rs = { git = "https://github.com/jimmygchen/mev-rs", rev = "dedc77a" } 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" } ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev = "12508c1f9b0c8f4bf4c5e9b6d441e840c1b37fd9" }
ssz_rs = "0.9.0" ssz_rs = "0.9.0"
tokio-stream = { version = "0.1.9", features = [ "sync" ] } tokio-stream = { workspace = true }
strum = "0.24.0" strum = { workspace = true }
keccak-hash = "0.10.0" keccak-hash = "0.10.0"
hash256-std-hasher = "0.15.2" hash256-std-hasher = "0.15.2"
triehash = "0.8.4" triehash = "0.8.4"
hash-db = "0.15.2" hash-db = "0.15.2"
pretty_reqwest_error = { path = "../../common/pretty_reqwest_error" } pretty_reqwest_error = { workspace = true }
arc-swap = "1.6.0"

View File

@ -5,6 +5,7 @@
//! deposit-contract functionality that the `beacon_node/eth1` crate already provides. //! deposit-contract functionality that the `beacon_node/eth1` crate already provides.
use crate::payload_cache::PayloadCache; use crate::payload_cache::PayloadCache;
use arc_swap::ArcSwapOption;
use auth::{strip_prefix, Auth, JwtKey}; use auth::{strip_prefix, Auth, JwtKey};
use builder_client::BuilderHttpClient; use builder_client::BuilderHttpClient;
pub use engine_api::EngineCapabilities; pub use engine_api::EngineCapabilities;
@ -301,7 +302,7 @@ type PayloadContentsRefTuple<'a, T> = (ExecutionPayloadRef<'a, T>, Option<&'a Bl
struct Inner<E: EthSpec> { struct Inner<E: EthSpec> {
engine: Arc<Engine>, engine: Arc<Engine>,
builder: Option<BuilderHttpClient>, builder: ArcSwapOption<BuilderHttpClient>,
execution_engine_forkchoice_lock: Mutex<()>, execution_engine_forkchoice_lock: Mutex<()>,
suggested_fee_recipient: Option<Address>, suggested_fee_recipient: Option<Address>,
proposer_preparation_data: Mutex<HashMap<u64, ProposerPreparationDataEntry>>, proposer_preparation_data: Mutex<HashMap<u64, ProposerPreparationDataEntry>>,
@ -453,25 +454,9 @@ impl<T: EthSpec> ExecutionLayer<T> {
Engine::new(api, executor.clone(), &log) Engine::new(api, executor.clone(), &log)
}; };
let builder = builder_url
.map(|url| {
let builder_client = BuilderHttpClient::new(url.clone(), builder_user_agent)
.map_err(Error::Builder)?;
info!(
log,
"Using external block builder";
"builder_url" => ?url,
"builder_profit_threshold" => builder_profit_threshold,
"local_user_agent" => builder_client.get_user_agent(),
);
Ok::<_, Error>(builder_client)
})
.transpose()?;
let inner = Inner { let inner = Inner {
engine: Arc::new(engine), engine: Arc::new(engine),
builder, builder: ArcSwapOption::empty(),
execution_engine_forkchoice_lock: <_>::default(), execution_engine_forkchoice_lock: <_>::default(),
suggested_fee_recipient, suggested_fee_recipient,
proposer_preparation_data: Mutex::new(HashMap::new()), proposer_preparation_data: Mutex::new(HashMap::new()),
@ -486,19 +471,45 @@ impl<T: EthSpec> ExecutionLayer<T> {
last_new_payload_errored: RwLock::new(false), last_new_payload_errored: RwLock::new(false),
}; };
Ok(Self { let el = Self {
inner: Arc::new(inner), inner: Arc::new(inner),
}) };
}
} if let Some(builder_url) = builder_url {
el.set_builder_url(builder_url, builder_user_agent)?;
}
Ok(el)
}
impl<T: EthSpec> ExecutionLayer<T> {
fn engine(&self) -> &Arc<Engine> { fn engine(&self) -> &Arc<Engine> {
&self.inner.engine &self.inner.engine
} }
pub fn builder(&self) -> &Option<BuilderHttpClient> { pub fn builder(&self) -> Option<Arc<BuilderHttpClient>> {
&self.inner.builder self.inner.builder.load_full()
}
/// Set the builder URL after initialization.
///
/// This is useful for breaking circular dependencies between mock ELs and mock builders in
/// tests.
pub fn set_builder_url(
&self,
builder_url: SensitiveUrl,
builder_user_agent: Option<String>,
) -> Result<(), Error> {
let builder_client = BuilderHttpClient::new(builder_url.clone(), builder_user_agent)
.map_err(Error::Builder)?;
info!(
self.log(),
"Using external block builder";
"builder_url" => ?builder_url,
"builder_profit_threshold" => self.inner.builder_profit_threshold.as_u128(),
"local_user_agent" => builder_client.get_user_agent(),
);
self.inner.builder.swap(Some(Arc::new(builder_client)));
Ok(())
} }
/// Cache a full payload, keyed on the `tree_hash_root` of the payload /// Cache a full payload, keyed on the `tree_hash_root` of the payload
@ -655,9 +666,9 @@ impl<T: EthSpec> ExecutionLayer<T> {
/// ///
/// This function is a wrapper over `Self::is_synced` that makes an additional /// This function is a wrapper over `Self::is_synced` that makes an additional
/// check for the execution layer sync status. Checks if the latest block has /// check for the execution layer sync status. Checks if the latest block has
/// a `block_number != 0`. /// a `block_number != 0` *if* the `current_slot` is also `> 0`.
/// Returns the `Self::is_synced` response if unable to get latest block. /// Returns the `Self::is_synced` response if unable to get latest block.
pub async fn is_synced_for_notifier(&self) -> bool { pub async fn is_synced_for_notifier(&self, current_slot: Slot) -> bool {
let synced = self.is_synced().await; let synced = self.is_synced().await;
if synced { if synced {
if let Ok(Some(block)) = self if let Ok(Some(block)) = self
@ -666,7 +677,7 @@ impl<T: EthSpec> ExecutionLayer<T> {
.get_block_by_number(BlockByNumberQuery::Tag(LATEST_TAG)) .get_block_by_number(BlockByNumberQuery::Tag(LATEST_TAG))
.await .await
{ {
if block.block_number == 0 { if block.block_number == 0 && current_slot > 0 {
return false; return false;
} }
} }
@ -1743,6 +1754,17 @@ impl<T: EthSpec> ExecutionLayer<T> {
} }
} }
pub async fn get_block_by_number(
&self,
query: BlockByNumberQuery<'_>,
) -> Result<Option<ExecutionBlock>, Error> {
self.engine()
.request(|engine| async move { engine.api.get_block_by_number(query).await })
.await
.map_err(Box::new)
.map_err(Error::EngineError)
}
pub async fn get_payload_by_hash_legacy( pub async fn get_payload_by_hash_legacy(
&self, &self,
hash: ExecutionBlockHash, hash: ExecutionBlockHash,

View File

@ -42,6 +42,11 @@ use types::{
ExecutionPayloadHeader, ForkName, ForkVersionedResponse, Hash256, Slot, Uint256, ExecutionPayloadHeader, ForkName, ForkVersionedResponse, Hash256, Slot, Uint256,
}; };
pub type MockBuilderServer = axum::Server<
hyper::server::conn::AddrIncoming,
axum::routing::IntoMakeService<axum::routing::Router>,
>;
#[derive(Clone)] #[derive(Clone)]
pub enum Operation { pub enum Operation {
FeeRecipient(Address), FeeRecipient(Address),
@ -166,19 +171,25 @@ impl BidStuff for BuilderBid {
} }
} }
pub struct TestingBuilder<E: EthSpec> { #[derive(Clone)]
server: BlindedBlockProviderServer<MockBuilder<E>>, pub struct MockBuilder<E: EthSpec> {
pub builder: MockBuilder<E>, el: ExecutionLayer<E>,
beacon_client: BeaconNodeHttpClient,
spec: ChainSpec,
context: Arc<Context>,
val_registration_cache: Arc<RwLock<HashMap<BlsPublicKey, SignedValidatorRegistration>>>,
builder_sk: SecretKey,
operations: Arc<RwLock<Vec<Operation>>>,
invalidate_signatures: Arc<RwLock<bool>>,
} }
impl<E: EthSpec> TestingBuilder<E> { impl<E: EthSpec> MockBuilder<E> {
pub fn new( pub fn new_for_testing(
mock_el_url: SensitiveUrl, mock_el_url: SensitiveUrl,
builder_url: SensitiveUrl,
beacon_url: SensitiveUrl, beacon_url: SensitiveUrl,
spec: ChainSpec, spec: ChainSpec,
executor: TaskExecutor, executor: TaskExecutor,
) -> Self { ) -> (Self, MockBuilderServer) {
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();
@ -207,39 +218,13 @@ impl<E: EthSpec> TestingBuilder<E> {
spec, spec,
context, context,
); );
let port = builder_url.full.port().unwrap(); let host: Ipv4Addr = Ipv4Addr::LOCALHOST;
let host: Ipv4Addr = builder_url let port = 0;
.full let provider = BlindedBlockProviderServer::new(host, port, builder.clone());
.host_str() let server = provider.serve();
.unwrap() (builder, server)
.to_string()
.parse()
.unwrap();
let server = BlindedBlockProviderServer::new(host, port, builder.clone());
Self { server, builder }
} }
pub async fn run(&self) {
let server = self.server.serve();
if let Err(err) = server.await {
println!("error while listening for incoming: {err}")
}
}
}
#[derive(Clone)]
pub struct MockBuilder<E: EthSpec> {
el: ExecutionLayer<E>,
beacon_client: BeaconNodeHttpClient,
spec: ChainSpec,
context: Arc<Context>,
val_registration_cache: Arc<RwLock<HashMap<BlsPublicKey, SignedValidatorRegistration>>>,
builder_sk: SecretKey,
operations: Arc<RwLock<Vec<Operation>>>,
invalidate_signatures: Arc<RwLock<bool>>,
}
impl<E: EthSpec> MockBuilder<E> {
pub fn new( pub fn new(
el: ExecutionLayer<E>, el: ExecutionLayer<E>,
beacon_client: BeaconNodeHttpClient, beacon_client: BeaconNodeHttpClient,

View File

@ -34,7 +34,6 @@ impl<T: EthSpec> MockExecutionLayer<T> {
Some(JwtKey::from_slice(&DEFAULT_JWT_SECRET).unwrap()), Some(JwtKey::from_slice(&DEFAULT_JWT_SECRET).unwrap()),
spec, spec,
None, None,
None,
) )
} }
@ -47,7 +46,6 @@ impl<T: EthSpec> MockExecutionLayer<T> {
builder_threshold: Option<u128>, builder_threshold: Option<u128>,
jwt_key: Option<JwtKey>, jwt_key: Option<JwtKey>,
spec: ChainSpec, spec: ChainSpec,
builder_url: Option<SensitiveUrl>,
kzg: Option<Kzg<T::Kzg>>, kzg: Option<Kzg<T::Kzg>>,
) -> Self { ) -> Self {
let handle = executor.handle().unwrap(); let handle = executor.handle().unwrap();
@ -72,7 +70,6 @@ impl<T: EthSpec> MockExecutionLayer<T> {
let config = Config { let config = Config {
execution_endpoints: vec![url], execution_endpoints: vec![url],
builder_url,
secret_files: vec![path], secret_files: vec![path],
suggested_fee_recipient: Some(Address::repeat_byte(42)), suggested_fee_recipient: Some(Address::repeat_byte(42)),
builder_profit_threshold: builder_threshold.unwrap_or(DEFAULT_BUILDER_THRESHOLD_WEI), builder_profit_threshold: builder_threshold.unwrap_or(DEFAULT_BUILDER_THRESHOLD_WEI),

View File

@ -31,7 +31,7 @@ pub use execution_block_generator::{
pub use hook::Hook; pub use hook::Hook;
pub use mock_builder::{ pub use mock_builder::{
convert_err, custom_err, from_ssz_rs, to_ssz_rs, Context as MockBuilderContext, MockBuilder, convert_err, custom_err, from_ssz_rs, to_ssz_rs, Context as MockBuilderContext, MockBuilder,
Operation, TestingBuilder, MockBuilderServer, Operation,
}; };
pub use mock_execution_layer::MockExecutionLayer; pub use mock_execution_layer::MockExecutionLayer;

View File

@ -2,23 +2,23 @@
name = "genesis" name = "genesis"
version = "0.2.0" version = "0.2.0"
authors = ["Paul Hauner <paul@paulhauner.com>"] authors = ["Paul Hauner <paul@paulhauner.com>"]
edition = "2021" edition = { workspace = true }
[dev-dependencies] [dev-dependencies]
eth1_test_rig = { path = "../../testing/eth1_test_rig" } eth1_test_rig = { workspace = true }
sensitive_url = { path = "../../common/sensitive_url" } sensitive_url = { workspace = true }
[dependencies] [dependencies]
futures = "0.3.7" futures = { workspace = true }
types = { path = "../../consensus/types"} types = { workspace = true }
environment = { path = "../../lighthouse/environment"} environment = { workspace = true }
eth1 = { path = "../eth1"} eth1 = { workspace = true }
rayon = "1.4.1" rayon = { workspace = true }
state_processing = { path = "../../consensus/state_processing" } state_processing = { workspace = true }
merkle_proof = { path = "../../consensus/merkle_proof" } merkle_proof = { workspace = true }
ethereum_ssz = "0.5.0" ethereum_ssz = { workspace = true }
ethereum_hashing = "1.0.0-beta.2" ethereum_hashing = { workspace = true }
tree_hash = "0.5.2" tree_hash = { workspace = true }
tokio = { version = "1.14.0", features = ["full"] } tokio = { workspace = true }
slog = "2.5.2" slog = { workspace = true }
int_to_bytes = { path = "../../consensus/int_to_bytes" } int_to_bytes = { workspace = true }

View File

@ -2,54 +2,53 @@
name = "http_api" name = "http_api"
version = "0.1.0" version = "0.1.0"
authors = ["Paul Hauner <paul@paulhauner.com>"] authors = ["Paul Hauner <paul@paulhauner.com>"]
edition = "2021" edition = { workspace = true }
autotests = false # using a single test binary compiles faster autotests = false # using a single test binary compiles faster
[dependencies] [dependencies]
warp = { version = "0.3.2", features = ["tls"] } warp = { workspace = true }
serde = { version = "1.0.116", features = ["derive"] } serde = { workspace = true }
tokio = { version = "1.14.0", features = ["macros", "sync"] } tokio = { workspace = true }
tokio-stream = { version = "0.1.3", features = ["sync"] } tokio-stream = { workspace = true }
types = { path = "../../consensus/types" } types = { workspace = true }
hex = "0.4.2" hex = { workspace = true }
beacon_chain = { path = "../beacon_chain" } beacon_chain = { workspace = true }
eth2 = { path = "../../common/eth2", features = ["lighthouse"] } eth2 = { workspace = true }
slog = "2.5.2" slog = { workspace = true }
network = { path = "../network" } network = { workspace = true }
lighthouse_network = { path = "../lighthouse_network" } lighthouse_network = { workspace = true }
eth1 = { path = "../eth1" } eth1 = { workspace = true }
state_processing = { path = "../../consensus/state_processing" } state_processing = { workspace = true }
lighthouse_version = { path = "../../common/lighthouse_version" } lighthouse_version = { workspace = true }
lighthouse_metrics = { path = "../../common/lighthouse_metrics" } lighthouse_metrics = { workspace = true }
lazy_static = "1.4.0" lazy_static = { workspace = true }
warp_utils = { path = "../../common/warp_utils" } warp_utils = { workspace = true }
slot_clock = { path = "../../common/slot_clock" } slot_clock = { workspace = true }
ethereum_ssz = "0.5.0" ethereum_ssz = { workspace = true }
bs58 = "0.4.0" bs58 = "0.4.0"
futures = "0.3.8" futures = { workspace = true }
execution_layer = { path = "../execution_layer" } execution_layer = { workspace = true }
parking_lot = "0.12.0" parking_lot = { workspace = true }
safe_arith = { path = "../../consensus/safe_arith" } safe_arith = { workspace = true }
task_executor = { path = "../../common/task_executor" } task_executor = { workspace = true }
lru = "0.7.7" lru = { workspace = true }
tree_hash = "0.5.2" tree_hash = { workspace = true }
sysinfo = "0.26.5" sysinfo = { workspace = true }
system_health = { path = "../../common/system_health" } system_health = { path = "../../common/system_health" }
directory = { path = "../../common/directory" } directory = { workspace = true }
logging = { path = "../../common/logging" } logging = { workspace = true }
ethereum_serde_utils = "0.5.0" ethereum_serde_utils = { workspace = true }
operation_pool = { path = "../operation_pool" } operation_pool = { workspace = true }
sensitive_url = { path = "../../common/sensitive_url" } sensitive_url = { workspace = true }
unused_port = { path = "../../common/unused_port" } store = { workspace = true }
store = { path = "../store" } bytes = { workspace = true }
bytes = "1.1.0" beacon_processor = { workspace = true }
beacon_processor = { path = "../beacon_processor" }
[dev-dependencies] [dev-dependencies]
environment = { path = "../../lighthouse/environment" } environment = { workspace = true }
serde_json = "1.0.58" serde_json = { workspace = true }
proto_array = { path = "../../consensus/proto_array" } proto_array = { workspace = true }
genesis = { path = "../genesis" } genesis = { workspace = true }
[[test]] [[test]]
name = "bn_http_api_tests" name = "bn_http_api_tests"

View File

@ -2869,12 +2869,8 @@ pub fn serve<T: BeaconChainTypes>(
})?; })?;
if let Some(peer_info) = network_globals.peers.read().peer_info(&peer_id) { if let Some(peer_info) = network_globals.peers.read().peer_info(&peer_id) {
let address = if let Some(socket_addr) = peer_info.seen_addresses().next() { let address = if let Some(multiaddr) = peer_info.seen_multiaddrs().next() {
let mut addr = lighthouse_network::Multiaddr::from(socket_addr.ip()); multiaddr.to_string()
addr.push(lighthouse_network::multiaddr::Protocol::Tcp(
socket_addr.port(),
));
addr.to_string()
} else if let Some(addr) = peer_info.listening_addresses().first() { } else if let Some(addr) = peer_info.listening_addresses().first() {
addr.to_string() addr.to_string()
} else { } else {
@ -2922,13 +2918,8 @@ pub fn serve<T: BeaconChainTypes>(
.peers() .peers()
.for_each(|(peer_id, peer_info)| { .for_each(|(peer_id, peer_info)| {
let address = let address =
if let Some(socket_addr) = peer_info.seen_addresses().next() { if let Some(multiaddr) = peer_info.seen_multiaddrs().next() {
let mut addr = multiaddr.to_string()
lighthouse_network::Multiaddr::from(socket_addr.ip());
addr.push(lighthouse_network::multiaddr::Protocol::Tcp(
socket_addr.port(),
));
addr.to_string()
} else if let Some(addr) = peer_info.listening_addresses().first() { } else if let Some(addr) = peer_info.listening_addresses().first() {
addr.to_string() addr.to_string()
} else { } else {
@ -3055,6 +3046,7 @@ pub fn serve<T: BeaconChainTypes>(
.and(warp::path::end()) .and(warp::path::end())
.and(not_while_syncing_filter.clone()) .and(not_while_syncing_filter.clone())
.and(warp::query::<api_types::ValidatorBlocksQuery>()) .and(warp::query::<api_types::ValidatorBlocksQuery>())
.and(warp::header::optional::<api_types::Accept>("accept"))
.and(task_spawner_filter.clone()) .and(task_spawner_filter.clone())
.and(chain_filter.clone()) .and(chain_filter.clone())
.and(log_filter.clone()) .and(log_filter.clone())
@ -3062,6 +3054,7 @@ pub fn serve<T: BeaconChainTypes>(
|endpoint_version: EndpointVersion, |endpoint_version: EndpointVersion,
slot: Slot, slot: Slot,
query: api_types::ValidatorBlocksQuery, query: api_types::ValidatorBlocksQuery,
accept_header: Option<api_types::Accept>,
task_spawner: TaskSpawner<T::EthSpec>, task_spawner: TaskSpawner<T::EthSpec>,
chain: Arc<BeaconChain<T>>, chain: Arc<BeaconChain<T>>,
log: Logger| { log: Logger| {
@ -3109,9 +3102,24 @@ pub fn serve<T: BeaconChainTypes>(
let block_contents = let block_contents =
build_block_contents::build_block_contents(fork_name, block, maybe_blobs)?; build_block_contents::build_block_contents(fork_name, block, maybe_blobs)?;
fork_versioned_response(endpoint_version, fork_name, block_contents) match accept_header {
.map(|response| warp::reply::json(&response).into_response()) Some(api_types::Accept::Ssz) => Response::builder()
.map(|res| add_consensus_version_header(res, fork_name)) .status(200)
.header("Content-Type", "application/octet-stream")
.body(block_contents.as_ssz_bytes().into())
.map(|res: Response<Bytes>| {
add_consensus_version_header(res, fork_name)
})
.map_err(|e| {
warp_utils::reject::custom_server_error(format!(
"failed to create response: {}",
e
))
}),
_ => fork_versioned_response(endpoint_version, fork_name, block_contents)
.map(|response| warp::reply::json(&response).into_response())
.map(|res| add_consensus_version_header(res, fork_name)),
}
}) })
}, },
); );
@ -3128,11 +3136,13 @@ pub fn serve<T: BeaconChainTypes>(
.and(warp::path::end()) .and(warp::path::end())
.and(not_while_syncing_filter.clone()) .and(not_while_syncing_filter.clone())
.and(warp::query::<api_types::ValidatorBlocksQuery>()) .and(warp::query::<api_types::ValidatorBlocksQuery>())
.and(warp::header::optional::<api_types::Accept>("accept"))
.and(task_spawner_filter.clone()) .and(task_spawner_filter.clone())
.and(chain_filter.clone()) .and(chain_filter.clone())
.then( .then(
|slot: Slot, |slot: Slot,
query: api_types::ValidatorBlocksQuery, query: api_types::ValidatorBlocksQuery,
accept_header: Option<api_types::Accept>,
task_spawner: TaskSpawner<T::EthSpec>, task_spawner: TaskSpawner<T::EthSpec>,
chain: Arc<BeaconChain<T>>| { chain: Arc<BeaconChain<T>>| {
task_spawner.spawn_async_with_rejection(Priority::P0, async move { task_spawner.spawn_async_with_rejection(Priority::P0, async move {
@ -3176,10 +3186,25 @@ pub fn serve<T: BeaconChainTypes>(
maybe_blobs, maybe_blobs,
)?; )?;
// Pose as a V2 endpoint so we return the fork `version`. match accept_header {
fork_versioned_response(V2, fork_name, block_contents) Some(api_types::Accept::Ssz) => Response::builder()
.map(|response| warp::reply::json(&response).into_response()) .status(200)
.map(|res| add_consensus_version_header(res, fork_name)) .header("Content-Type", "application/octet-stream")
.body(block_contents.as_ssz_bytes().into())
.map(|res: Response<Bytes>| {
add_consensus_version_header(res, fork_name)
})
.map_err(|e| {
warp_utils::reject::custom_server_error(format!(
"failed to create response: {}",
e
))
}),
// Pose as a V2 endpoint so we return the fork `version`.
_ => fork_versioned_response(V2, fork_name, block_contents)
.map(|response| warp::reply::json(&response).into_response())
.map(|res| add_consensus_version_header(res, fork_name)),
}
}) })
}, },
); );
@ -3697,12 +3722,13 @@ pub fn serve<T: BeaconChainTypes>(
// send the response back to our original HTTP request // send the response back to our original HTTP request
// task via a channel. // task via a channel.
let builder_future = async move { let builder_future = async move {
let builder = chain let arc_builder = chain
.execution_layer .execution_layer
.as_ref() .as_ref()
.ok_or(BeaconChainError::ExecutionLayerMissing) .ok_or(BeaconChainError::ExecutionLayerMissing)
.map_err(warp_utils::reject::beacon_chain_error)? .map_err(warp_utils::reject::beacon_chain_error)?
.builder() .builder();
let builder = arc_builder
.as_ref() .as_ref()
.ok_or(BeaconChainError::BuilderMissing) .ok_or(BeaconChainError::BuilderMissing)
.map_err(warp_utils::reject::beacon_chain_error)?; .map_err(warp_utils::reject::beacon_chain_error)?;
@ -4363,7 +4389,8 @@ pub fn serve<T: BeaconChainTypes>(
.then( .then(
|task_spawner: TaskSpawner<T::EthSpec>, chain: Arc<BeaconChain<T>>| { |task_spawner: TaskSpawner<T::EthSpec>, chain: Arc<BeaconChain<T>>| {
task_spawner.spawn_async_with_rejection(Priority::P1, async move { task_spawner.spawn_async_with_rejection(Priority::P1, async move {
let merge_readiness = chain.check_merge_readiness().await; let current_slot = chain.slot_clock.now_or_genesis().unwrap_or(Slot::new(0));
let merge_readiness = chain.check_merge_readiness(current_slot).await;
Ok::<_, warp::reject::Rejection>( Ok::<_, warp::reject::Rejection>(
warp::reply::json(&api_types::GenericResponse::from(merge_readiness)) warp::reply::json(&api_types::GenericResponse::from(merge_readiness))
.into_response(), .into_response(),

View File

@ -126,17 +126,9 @@ pub async fn create_api_server<T: BeaconChainTypes>(
test_runtime: &TestRuntime, test_runtime: &TestRuntime,
log: Logger, log: Logger,
) -> ApiServer<T::EthSpec, impl Future<Output = ()>> { ) -> ApiServer<T::EthSpec, impl Future<Output = ()>> {
// Get a random unused port. // Use port 0 to allocate a new unused port.
let port = unused_port::unused_tcp4_port().unwrap(); let port = 0;
create_api_server_on_port(chain, test_runtime, log, port).await
}
pub async fn create_api_server_on_port<T: BeaconChainTypes>(
chain: Arc<BeaconChain<T>>,
test_runtime: &TestRuntime,
log: Logger,
port: u16,
) -> ApiServer<T::EthSpec, impl Future<Output = ()>> {
let (network_senders, network_receivers) = NetworkSenders::new(); let (network_senders, network_receivers) = NetworkSenders::new();
// Default metadata // Default metadata
@ -149,8 +141,6 @@ pub async fn create_api_server_on_port<T: BeaconChainTypes>(
let enr = EnrBuilder::new("v4").build(&enr_key).unwrap(); let enr = EnrBuilder::new("v4").build(&enr_key).unwrap();
let network_globals = Arc::new(NetworkGlobals::new( let network_globals = Arc::new(NetworkGlobals::new(
enr.clone(), enr.clone(),
Some(TCP_PORT),
None,
meta_data, meta_data,
vec![], vec![],
false, false,

View File

@ -10,15 +10,14 @@ use eth2::{
types::{BlockId as CoreBlockId, ForkChoiceNode, StateId as CoreStateId, *}, types::{BlockId as CoreBlockId, ForkChoiceNode, StateId as CoreStateId, *},
BeaconNodeHttpClient, Error, StatusCode, Timeouts, BeaconNodeHttpClient, Error, StatusCode, Timeouts,
}; };
use execution_layer::test_utils::TestingBuilder;
use execution_layer::test_utils::DEFAULT_BUILDER_THRESHOLD_WEI;
use execution_layer::test_utils::{ use execution_layer::test_utils::{
Operation, DEFAULT_BUILDER_PAYLOAD_VALUE_WEI, DEFAULT_MOCK_EL_PAYLOAD_VALUE_WEI, MockBuilder, Operation, DEFAULT_BUILDER_PAYLOAD_VALUE_WEI, DEFAULT_BUILDER_THRESHOLD_WEI,
DEFAULT_MOCK_EL_PAYLOAD_VALUE_WEI,
}; };
use futures::stream::{Stream, StreamExt}; use futures::stream::{Stream, StreamExt};
use futures::FutureExt; use futures::FutureExt;
use http_api::{ use http_api::{
test_utils::{create_api_server, create_api_server_on_port, ApiServer}, test_utils::{create_api_server, ApiServer},
BlockId, StateId, BlockId, StateId,
}; };
use lighthouse_network::{Enr, EnrExt, PeerId}; use lighthouse_network::{Enr, EnrExt, PeerId};
@ -73,7 +72,7 @@ struct ApiTester {
network_rx: NetworkReceivers<E>, network_rx: NetworkReceivers<E>,
local_enr: Enr, local_enr: Enr,
external_peer_id: PeerId, external_peer_id: PeerId,
mock_builder: Option<Arc<TestingBuilder<E>>>, mock_builder: Option<Arc<MockBuilder<E>>>,
} }
struct ApiTesterConfig { struct ApiTesterConfig {
@ -120,24 +119,28 @@ impl ApiTester {
} }
pub async fn new_from_config(config: ApiTesterConfig) -> Self { pub async fn new_from_config(config: ApiTesterConfig) -> Self {
// Get a random unused port
let spec = config.spec; let spec = config.spec;
let port = unused_port::unused_tcp4_port().unwrap();
let beacon_url = SensitiveUrl::parse(format!("http://127.0.0.1:{port}").as_str()).unwrap();
let harness = Arc::new( let mut harness = BeaconChainHarness::builder(MainnetEthSpec)
BeaconChainHarness::builder(MainnetEthSpec) .spec(spec.clone())
.spec(spec.clone()) .chain_config(ChainConfig {
.chain_config(ChainConfig { reconstruct_historic_states: config.retain_historic_states,
reconstruct_historic_states: config.retain_historic_states, ..ChainConfig::default()
..ChainConfig::default() })
}) .logger(logging::test_logger())
.logger(logging::test_logger()) .deterministic_keypairs(VALIDATOR_COUNT)
.deterministic_keypairs(VALIDATOR_COUNT) .fresh_ephemeral_store()
.fresh_ephemeral_store() .mock_execution_layer_with_config(config.builder_threshold)
.mock_execution_layer_with_builder(beacon_url.clone(), config.builder_threshold) .build();
.build(),
); harness
.mock_execution_layer
.as_ref()
.unwrap()
.server
.execution_block_generator()
.move_to_terminal_block()
.unwrap();
harness.advance_slot(); harness.advance_slot();
@ -247,29 +250,40 @@ impl ApiTester {
let ApiServer { let ApiServer {
server, server,
listening_socket: _, listening_socket,
network_rx, network_rx,
local_enr, local_enr,
external_peer_id, external_peer_id,
} = create_api_server_on_port(chain.clone(), &harness.runtime, log, port).await; } = create_api_server(chain.clone(), &harness.runtime, log).await;
harness.runtime.task_executor.spawn(server, "api_server"); harness.runtime.task_executor.spawn(server, "api_server");
// Late-initalize the mock builder now that the mock execution node and beacon API ports
// have been allocated.
let beacon_api_port = listening_socket.port();
let beacon_url =
SensitiveUrl::parse(format!("http://127.0.0.1:{beacon_api_port}").as_str()).unwrap();
let mock_builder_server = harness.set_mock_builder(beacon_url.clone());
// Start the mock builder service prior to building the chain out.
harness.runtime.task_executor.spawn(
async move {
if let Err(e) = mock_builder_server.await {
panic!("error in mock builder server: {e:?}");
}
},
"mock_builder_server",
);
let mock_builder = harness.mock_builder.clone();
let client = BeaconNodeHttpClient::new( let client = BeaconNodeHttpClient::new(
beacon_url, beacon_url,
Timeouts::set_all(Duration::from_secs(SECONDS_PER_SLOT)), Timeouts::set_all(Duration::from_secs(SECONDS_PER_SLOT)),
); );
let builder_ref = harness.mock_builder.as_ref().unwrap().clone();
harness.runtime.task_executor.spawn(
async move { builder_ref.run().await },
"mock_builder_server",
);
let mock_builder = harness.mock_builder.clone();
Self { Self {
harness, harness: Arc::new(harness),
chain, chain,
client, client,
next_block, next_block,
@ -383,7 +397,6 @@ impl ApiTester {
.mock_builder .mock_builder
.as_ref() .as_ref()
.unwrap() .unwrap()
.builder
.add_operation(Operation::Value(Uint256::from( .add_operation(Operation::Value(Uint256::from(
DEFAULT_BUILDER_THRESHOLD_WEI, DEFAULT_BUILDER_THRESHOLD_WEI,
))); )));
@ -406,7 +419,6 @@ impl ApiTester {
.mock_builder .mock_builder
.as_ref() .as_ref()
.unwrap() .unwrap()
.builder
.add_operation(Operation::Value(Uint256::from( .add_operation(Operation::Value(Uint256::from(
DEFAULT_BUILDER_PAYLOAD_VALUE_WEI, DEFAULT_BUILDER_PAYLOAD_VALUE_WEI,
))); )));
@ -2528,6 +2540,74 @@ impl ApiTester {
self self
} }
pub async fn test_block_production_ssz(self) -> Self {
let fork = self.chain.canonical_head.cached_head().head_fork();
let genesis_validators_root = self.chain.genesis_validators_root;
for _ in 0..E::slots_per_epoch() * 3 {
let slot = self.chain.slot().unwrap();
let epoch = self.chain.epoch().unwrap();
let proposer_pubkey_bytes = self
.client
.get_validator_duties_proposer(epoch)
.await
.unwrap()
.data
.into_iter()
.find(|duty| duty.slot == slot)
.map(|duty| duty.pubkey)
.unwrap();
let proposer_pubkey = (&proposer_pubkey_bytes).try_into().unwrap();
let sk = self
.validator_keypairs()
.iter()
.find(|kp| kp.pk == proposer_pubkey)
.map(|kp| kp.sk.clone())
.unwrap();
let randao_reveal = {
let domain = self.chain.spec.get_domain(
epoch,
Domain::Randao,
&fork,
genesis_validators_root,
);
let message = epoch.signing_root(domain);
sk.sign(message).into()
};
let block_bytes = self
.client
.get_validator_blocks_ssz::<E, FullPayload<E>>(slot, &randao_reveal, None)
.await
.unwrap()
.expect("block bytes");
let block_contents =
BlockContents::<E, FullPayload<E>>::from_ssz_bytes(&block_bytes, &self.chain.spec)
.expect("block contents bytes can be decoded");
let signed_block_contents =
block_contents.sign(&sk, &fork, genesis_validators_root, &self.chain.spec);
self.client
.post_beacon_blocks_ssz(&signed_block_contents)
.await
.unwrap();
assert_eq!(
self.chain.head_beacon_block().as_ref(),
signed_block_contents.signed_block()
);
self.chain.slot_clock.set_slot(slot.as_u64() + 1);
}
self
}
pub async fn test_block_production_no_verify_randao(self) -> Self { pub async fn test_block_production_no_verify_randao(self) -> Self {
for _ in 0..E::slots_per_epoch() { for _ in 0..E::slots_per_epoch() {
let slot = self.chain.slot().unwrap(); let slot = self.chain.slot().unwrap();
@ -2713,12 +2793,18 @@ impl ApiTester {
sk.sign(message).into() sk.sign(message).into()
}; };
let block_contents = self let block_contents_bytes = self
.client .client
.get_validator_blinded_blocks::<E, Payload>(slot, &randao_reveal, None) .get_validator_blinded_blocks_ssz::<E, Payload>(slot, &randao_reveal, None)
.await .await
.unwrap() .unwrap()
.data; .expect("block bytes");
let block_contents = BlockContents::<E, Payload>::from_ssz_bytes(
&block_contents_bytes,
&self.chain.spec,
)
.expect("block contents bytes can be decoded");
let signed_block_contents = let signed_block_contents =
block_contents.sign(&sk, &fork, genesis_validators_root, &self.chain.spec); block_contents.sign(&sk, &fork, genesis_validators_root, &self.chain.spec);
@ -3309,6 +3395,7 @@ impl ApiTester {
.unwrap() .unwrap()
.get_payload_by_root(&payload.tree_hash_root()) .get_payload_by_root(&payload.tree_hash_root())
.is_none()); .is_none());
self self
} }
@ -3317,7 +3404,6 @@ impl ApiTester {
self.mock_builder self.mock_builder
.as_ref() .as_ref()
.unwrap() .unwrap()
.builder
.add_operation(Operation::GasLimit(30_000_000)); .add_operation(Operation::GasLimit(30_000_000));
let slot = self.chain.slot().unwrap(); let slot = self.chain.slot().unwrap();
@ -3361,7 +3447,6 @@ impl ApiTester {
self.mock_builder self.mock_builder
.as_ref() .as_ref()
.unwrap() .unwrap()
.builder
.add_operation(Operation::FeeRecipient(test_fee_recipient)); .add_operation(Operation::FeeRecipient(test_fee_recipient));
let slot = self.chain.slot().unwrap(); let slot = self.chain.slot().unwrap();
@ -3404,7 +3489,6 @@ impl ApiTester {
self.mock_builder self.mock_builder
.as_ref() .as_ref()
.unwrap() .unwrap()
.builder
.add_operation(Operation::ParentHash(invalid_parent_hash)); .add_operation(Operation::ParentHash(invalid_parent_hash));
let slot = self.chain.slot().unwrap(); let slot = self.chain.slot().unwrap();
@ -3454,7 +3538,6 @@ impl ApiTester {
self.mock_builder self.mock_builder
.as_ref() .as_ref()
.unwrap() .unwrap()
.builder
.add_operation(Operation::PrevRandao(invalid_prev_randao)); .add_operation(Operation::PrevRandao(invalid_prev_randao));
let slot = self.chain.slot().unwrap(); let slot = self.chain.slot().unwrap();
@ -3500,7 +3583,6 @@ impl ApiTester {
self.mock_builder self.mock_builder
.as_ref() .as_ref()
.unwrap() .unwrap()
.builder
.add_operation(Operation::BlockNumber(invalid_block_number)); .add_operation(Operation::BlockNumber(invalid_block_number));
let slot = self.chain.slot().unwrap(); let slot = self.chain.slot().unwrap();
@ -3548,7 +3630,6 @@ impl ApiTester {
self.mock_builder self.mock_builder
.as_ref() .as_ref()
.unwrap() .unwrap()
.builder
.add_operation(Operation::Timestamp(invalid_timestamp)); .add_operation(Operation::Timestamp(invalid_timestamp));
let slot = self.chain.slot().unwrap(); let slot = self.chain.slot().unwrap();
@ -3589,11 +3670,7 @@ impl ApiTester {
} }
pub async fn test_payload_rejects_invalid_signature(self) -> Self { pub async fn test_payload_rejects_invalid_signature(self) -> Self {
self.mock_builder self.mock_builder.as_ref().unwrap().invalid_signatures();
.as_ref()
.unwrap()
.builder
.invalid_signatures();
let slot = self.chain.slot().unwrap(); let slot = self.chain.slot().unwrap();
let epoch = self.chain.epoch().unwrap(); let epoch = self.chain.epoch().unwrap();
@ -3878,7 +3955,6 @@ impl ApiTester {
self.mock_builder self.mock_builder
.as_ref() .as_ref()
.unwrap() .unwrap()
.builder
.add_operation(Operation::Value(Uint256::from( .add_operation(Operation::Value(Uint256::from(
DEFAULT_BUILDER_THRESHOLD_WEI - 1, DEFAULT_BUILDER_THRESHOLD_WEI - 1,
))); )));
@ -3916,7 +3992,6 @@ impl ApiTester {
self.mock_builder self.mock_builder
.as_ref() .as_ref()
.unwrap() .unwrap()
.builder
.add_operation(Operation::Value(Uint256::from( .add_operation(Operation::Value(Uint256::from(
DEFAULT_MOCK_EL_PAYLOAD_VALUE_WEI + 1, DEFAULT_MOCK_EL_PAYLOAD_VALUE_WEI + 1,
))); )));
@ -3954,7 +4029,6 @@ impl ApiTester {
self.mock_builder self.mock_builder
.as_ref() .as_ref()
.unwrap() .unwrap()
.builder
.add_operation(Operation::Value(Uint256::from( .add_operation(Operation::Value(Uint256::from(
DEFAULT_MOCK_EL_PAYLOAD_VALUE_WEI, DEFAULT_MOCK_EL_PAYLOAD_VALUE_WEI,
))); )));
@ -3992,7 +4066,6 @@ impl ApiTester {
self.mock_builder self.mock_builder
.as_ref() .as_ref()
.unwrap() .unwrap()
.builder
.add_operation(Operation::Value(Uint256::from( .add_operation(Operation::Value(Uint256::from(
DEFAULT_MOCK_EL_PAYLOAD_VALUE_WEI - 1, DEFAULT_MOCK_EL_PAYLOAD_VALUE_WEI - 1,
))); )));
@ -4030,7 +4103,6 @@ impl ApiTester {
self.mock_builder self.mock_builder
.as_ref() .as_ref()
.unwrap() .unwrap()
.builder
.add_operation(Operation::Value(Uint256::from( .add_operation(Operation::Value(Uint256::from(
DEFAULT_MOCK_EL_PAYLOAD_VALUE_WEI + 1, DEFAULT_MOCK_EL_PAYLOAD_VALUE_WEI + 1,
))); )));
@ -4067,7 +4139,6 @@ impl ApiTester {
self.mock_builder self.mock_builder
.as_ref() .as_ref()
.unwrap() .unwrap()
.builder
.add_operation(Operation::Value(Uint256::from( .add_operation(Operation::Value(Uint256::from(
DEFAULT_MOCK_EL_PAYLOAD_VALUE_WEI + 1, DEFAULT_MOCK_EL_PAYLOAD_VALUE_WEI + 1,
))); )));
@ -4104,7 +4175,6 @@ impl ApiTester {
self.mock_builder self.mock_builder
.as_ref() .as_ref()
.unwrap() .unwrap()
.builder
.add_operation(Operation::Value(Uint256::from( .add_operation(Operation::Value(Uint256::from(
DEFAULT_MOCK_EL_PAYLOAD_VALUE_WEI + 1, DEFAULT_MOCK_EL_PAYLOAD_VALUE_WEI + 1,
))); )));
@ -4112,7 +4182,6 @@ impl ApiTester {
self.mock_builder self.mock_builder
.as_ref() .as_ref()
.unwrap() .unwrap()
.builder
.add_operation(Operation::WithdrawalsRoot(Hash256::repeat_byte(0x42))); .add_operation(Operation::WithdrawalsRoot(Hash256::repeat_byte(0x42)));
let slot = self.chain.slot().unwrap(); let slot = self.chain.slot().unwrap();
@ -4976,6 +5045,20 @@ async fn block_production_verify_randao_invalid() {
.await; .await;
} }
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn block_production_ssz_full_payload() {
ApiTester::new().await.test_block_production_ssz().await;
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn block_production_ssz_with_skip_slots() {
ApiTester::new()
.await
.skip_slots(E::slots_per_epoch() * 2)
.test_block_production_ssz()
.await;
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn blinded_block_production_full_payload_premerge() { async fn blinded_block_production_full_payload_premerge() {
ApiTester::new() ApiTester::new()

View File

@ -2,25 +2,25 @@
name = "http_metrics" name = "http_metrics"
version = "0.1.0" version = "0.1.0"
authors = ["Paul Hauner <paul@paulhauner.com>"] authors = ["Paul Hauner <paul@paulhauner.com>"]
edition = "2021" edition = { workspace = true }
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
warp = "0.3.2" warp = { workspace = true }
serde = { version = "1.0.116", features = ["derive"] } serde = { workspace = true }
slog = "2.5.2" slog = { workspace = true }
beacon_chain = { path = "../beacon_chain" } beacon_chain = { workspace = true }
store = { path = "../store" } store = { workspace = true }
lighthouse_network = { path = "../lighthouse_network" } lighthouse_network = { workspace = true }
slot_clock = { path = "../../common/slot_clock" } slot_clock = { workspace = true }
lighthouse_metrics = { path = "../../common/lighthouse_metrics" } lighthouse_metrics = { workspace = true }
lighthouse_version = { path = "../../common/lighthouse_version" } lighthouse_version = { workspace = true }
warp_utils = { path = "../../common/warp_utils" } warp_utils = { workspace = true }
malloc_utils = { path = "../../common/malloc_utils" } malloc_utils = { workspace = true }
[dev-dependencies] [dev-dependencies]
tokio = { version = "1.14.0", features = ["sync"] } tokio = { workspace = true }
reqwest = { version = "0.11.0", features = ["json"] } reqwest = { workspace = true }
environment = { path = "../../lighthouse/environment" } environment = { workspace = true }
types = { path = "../../consensus/types" } types = { workspace = true }

View File

@ -2,63 +2,64 @@
name = "lighthouse_network" name = "lighthouse_network"
version = "0.2.0" version = "0.2.0"
authors = ["Sigma Prime <contact@sigmaprime.io>"] authors = ["Sigma Prime <contact@sigmaprime.io>"]
edition = "2021" edition = { workspace = true }
[dependencies] [dependencies]
discv5 = { version = "0.3.1", features = ["libp2p"] } discv5 = { workspace = true }
unsigned-varint = { version = "0.6.0", features = ["codec"] } unsigned-varint = { version = "0.6", features = ["codec"] }
types = { path = "../../consensus/types" } ssz_types = { workspace = true }
ssz_types = "0.5.4" types = { workspace = true }
serde = { version = "1.0.116", features = ["derive"] } serde = { workspace = true }
serde_derive = "1.0.116" serde_derive = "1"
ethereum_ssz = "0.5.0" ethereum_ssz = { workspace = true }
ethereum_ssz_derive = "0.5.3" ethereum_ssz_derive = { workspace = true }
tree_hash = "0.5.2" tree_hash = { workspace = true }
tree_hash_derive = "0.5.0" tree_hash_derive = { workspace = true }
slog = { version = "2.5.2", features = ["max_level_trace"] } slog = { workspace = true }
lighthouse_version = { path = "../../common/lighthouse_version" } lighthouse_version = { workspace = true }
tokio = { version = "1.14.0", features = ["time", "macros"] } tokio = { workspace = true }
futures = "0.3.7" futures = { workspace = true }
error-chain = "0.12.4" error-chain = { workspace = true }
dirs = "3.0.1" dirs = { workspace = true }
fnv = "1.0.7" fnv = { workspace = true }
lazy_static = "1.4.0" lazy_static = { workspace = true }
lighthouse_metrics = { path = "../../common/lighthouse_metrics" } lighthouse_metrics = { workspace = true }
smallvec = "1.6.1" smallvec = { workspace = true }
tokio-io-timeout = "1.1.1" tokio-io-timeout = "1"
lru = "0.7.1" lru = { workspace = true }
lru_cache = { path = "../../common/lru_cache" } lru_cache = { workspace = true }
parking_lot = "0.12.0" parking_lot = { workspace = true }
sha2 = "0.10" sha2 = { workspace = true }
snap = "1.0.1" snap = { workspace = true }
hex = "0.4.2" hex = { workspace = true }
tokio-util = { version = "0.6.2", features = ["codec", "compat", "time"] } tokio-util = { workspace = true }
tiny-keccak = "2.0.2" tiny-keccak = "2"
task_executor = { path = "../../common/task_executor" } task_executor = { workspace = true }
rand = "0.8.5" rand = { workspace = true }
directory = { path = "../../common/directory" } directory = { workspace = true }
regex = "1.5.5" regex = { workspace = true }
strum = { version = "0.24.0", features = ["derive"] } strum = { workspace = true }
superstruct = "0.5.0" superstruct = { workspace = true }
prometheus-client = "0.21.0" prometheus-client = "0.21.0"
unused_port = { path = "../../common/unused_port" } unused_port = { workspace = true }
delay_map = "0.3.0" delay_map = { workspace = true }
void = "1" void = "1"
libp2p-quic= { version = "0.9.2", features=["tokio"]}
libp2p-mplex = "0.40.0" libp2p-mplex = "0.40.0"
[dependencies.libp2p] [dependencies.libp2p]
version = "0.52" version = "0.52"
default-features = false default-features = false
features = ["websocket", "identify", "yamux", "noise", "gossipsub", "dns", "tcp", "tokio", "plaintext", "secp256k1", "macros", "ecdsa"] features = ["identify", "yamux", "noise", "gossipsub", "dns", "tcp", "tokio", "plaintext", "secp256k1", "macros", "ecdsa"]
[dev-dependencies] [dev-dependencies]
slog-term = "2.6.0" slog-term = { workspace = true }
slog-async = "2.5.0" slog-async = { workspace = true }
tempfile = "3.1.0" tempfile = { workspace = true }
exit-future = "0.2.0" exit-future = { workspace = true }
void = "1" quickcheck = { workspace = true }
quickcheck = "0.9.2" quickcheck_macros = { workspace = true }
quickcheck_macros = "0.9.1"
[features] [features]
libp2p-websocket = [] libp2p-websocket = []

View File

@ -58,18 +58,24 @@ pub struct Config {
/// that no discovery address has been set in the CLI args. /// that no discovery address has been set in the CLI args.
pub enr_address: (Option<Ipv4Addr>, Option<Ipv6Addr>), pub enr_address: (Option<Ipv4Addr>, Option<Ipv6Addr>),
/// The udp4 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<u16>,
/// The tcp4 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>,
/// 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<u16>,
/// The udp6 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<u16>,
/// The tcp6 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<u16>,
/// The quic ipv6 port to broadcast to peers in order to reach back for libp2p services.
pub enr_quic6_port: Option<u16>,
/// Target number of connected peers. /// Target number of connected peers.
pub target_peers: usize, pub target_peers: usize,
@ -102,6 +108,9 @@ pub struct Config {
/// Disables the discovery protocol from starting. /// Disables the discovery protocol from starting.
pub disable_discovery: bool, pub disable_discovery: bool,
/// Disables quic support.
pub disable_quic_support: bool,
/// Attempt to construct external port mappings with UPnP. /// Attempt to construct external port mappings with UPnP.
pub upnp_enabled: bool, pub upnp_enabled: bool,
@ -149,57 +158,76 @@ impl Config {
/// Sets the listening address to use an ipv4 address. The discv5 ip_mode and table filter are /// Sets the listening address to use an ipv4 address. The discv5 ip_mode and table filter are
/// adjusted accordingly to ensure addresses that are present in the enr are globally /// adjusted accordingly to ensure addresses that are present in the enr are globally
/// reachable. /// reachable.
pub fn set_ipv4_listening_address(&mut self, addr: Ipv4Addr, tcp_port: u16, udp_port: u16) { pub fn set_ipv4_listening_address(
&mut self,
addr: Ipv4Addr,
tcp_port: u16,
disc_port: u16,
quic_port: u16,
) {
self.listen_addresses = ListenAddress::V4(ListenAddr { self.listen_addresses = ListenAddress::V4(ListenAddr {
addr, addr,
udp_port, disc_port,
quic_port,
tcp_port, tcp_port,
}); });
self.discv5_config.listen_config = discv5::ListenConfig::from_ip(addr.into(), udp_port); self.discv5_config.listen_config = discv5::ListenConfig::from_ip(addr.into(), disc_port);
self.discv5_config.table_filter = |enr| enr.ip4().as_ref().map_or(false, is_global_ipv4) self.discv5_config.table_filter = |enr| enr.ip4().as_ref().map_or(false, is_global_ipv4)
} }
/// Sets the listening address to use an ipv6 address. The discv5 ip_mode and table filter is /// Sets the listening address to use an ipv6 address. The discv5 ip_mode and table filter is
/// adjusted accordingly to ensure addresses that are present in the enr are globally /// adjusted accordingly to ensure addresses that are present in the enr are globally
/// reachable. /// reachable.
pub fn set_ipv6_listening_address(&mut self, addr: Ipv6Addr, tcp_port: u16, udp_port: u16) { pub fn set_ipv6_listening_address(
&mut self,
addr: Ipv6Addr,
tcp_port: u16,
disc_port: u16,
quic_port: u16,
) {
self.listen_addresses = ListenAddress::V6(ListenAddr { self.listen_addresses = ListenAddress::V6(ListenAddr {
addr, addr,
udp_port, disc_port,
quic_port,
tcp_port, tcp_port,
}); });
self.discv5_config.listen_config = discv5::ListenConfig::from_ip(addr.into(), udp_port); self.discv5_config.listen_config = discv5::ListenConfig::from_ip(addr.into(), disc_port);
self.discv5_config.table_filter = |enr| enr.ip6().as_ref().map_or(false, is_global_ipv6) self.discv5_config.table_filter = |enr| enr.ip6().as_ref().map_or(false, is_global_ipv6)
} }
/// Sets the listening address to use both an ipv4 and ipv6 address. The discv5 ip_mode and /// Sets the listening address to use both an ipv4 and ipv6 address. The discv5 ip_mode and
/// table filter is adjusted accordingly to ensure addresses that are present in the enr are /// table filter is adjusted accordingly to ensure addresses that are present in the enr are
/// globally reachable. /// globally reachable.
#[allow(clippy::too_many_arguments)]
pub fn set_ipv4_ipv6_listening_addresses( pub fn set_ipv4_ipv6_listening_addresses(
&mut self, &mut self,
v4_addr: Ipv4Addr, v4_addr: Ipv4Addr,
tcp4_port: u16, tcp4_port: u16,
udp4_port: u16, disc4_port: u16,
quic4_port: u16,
v6_addr: Ipv6Addr, v6_addr: Ipv6Addr,
tcp6_port: u16, tcp6_port: u16,
udp6_port: u16, disc6_port: u16,
quic6_port: u16,
) { ) {
self.listen_addresses = ListenAddress::DualStack( self.listen_addresses = ListenAddress::DualStack(
ListenAddr { ListenAddr {
addr: v4_addr, addr: v4_addr,
udp_port: udp4_port, disc_port: disc4_port,
quic_port: quic4_port,
tcp_port: tcp4_port, tcp_port: tcp4_port,
}, },
ListenAddr { ListenAddr {
addr: v6_addr, addr: v6_addr,
udp_port: udp6_port, disc_port: disc6_port,
quic_port: quic6_port,
tcp_port: tcp6_port, tcp_port: tcp6_port,
}, },
); );
self.discv5_config.listen_config = discv5::ListenConfig::default() self.discv5_config.listen_config = discv5::ListenConfig::default()
.with_ipv4(v4_addr, udp4_port) .with_ipv4(v4_addr, disc4_port)
.with_ipv6(v6_addr, udp6_port); .with_ipv6(v6_addr, disc6_port);
self.discv5_config.table_filter = |enr| match (&enr.ip4(), &enr.ip6()) { self.discv5_config.table_filter = |enr| match (&enr.ip4(), &enr.ip6()) {
(None, None) => false, (None, None) => false,
@ -213,27 +241,32 @@ impl Config {
match listen_addr { match listen_addr {
ListenAddress::V4(ListenAddr { ListenAddress::V4(ListenAddr {
addr, addr,
udp_port, disc_port,
quic_port,
tcp_port, tcp_port,
}) => self.set_ipv4_listening_address(addr, tcp_port, udp_port), }) => self.set_ipv4_listening_address(addr, tcp_port, disc_port, quic_port),
ListenAddress::V6(ListenAddr { ListenAddress::V6(ListenAddr {
addr, addr,
udp_port, disc_port,
quic_port,
tcp_port, tcp_port,
}) => self.set_ipv6_listening_address(addr, tcp_port, udp_port), }) => self.set_ipv6_listening_address(addr, tcp_port, disc_port, quic_port),
ListenAddress::DualStack( ListenAddress::DualStack(
ListenAddr { ListenAddr {
addr: ip4addr, addr: ip4addr,
udp_port: udp4_port, disc_port: disc4_port,
quic_port: quic4_port,
tcp_port: tcp4_port, tcp_port: tcp4_port,
}, },
ListenAddr { ListenAddr {
addr: ip6addr, addr: ip6addr,
udp_port: udp6_port, disc_port: disc6_port,
quic_port: quic6_port,
tcp_port: tcp6_port, tcp_port: tcp6_port,
}, },
) => self.set_ipv4_ipv6_listening_addresses( ) => self.set_ipv4_ipv6_listening_addresses(
ip4addr, tcp4_port, udp4_port, ip6addr, tcp6_port, udp6_port, ip4addr, tcp4_port, disc4_port, quic4_port, ip6addr, tcp6_port, disc6_port,
quic6_port,
), ),
} }
} }
@ -272,7 +305,8 @@ impl Default for Config {
); );
let listen_addresses = ListenAddress::V4(ListenAddr { let listen_addresses = ListenAddress::V4(ListenAddr {
addr: Ipv4Addr::UNSPECIFIED, addr: Ipv4Addr::UNSPECIFIED,
udp_port: 9000, disc_port: 9000,
quic_port: 9001,
tcp_port: 9000, tcp_port: 9000,
}); });
@ -305,10 +339,11 @@ impl Default for Config {
network_dir, network_dir,
listen_addresses, listen_addresses,
enr_address: (None, None), enr_address: (None, None),
enr_udp4_port: None, enr_udp4_port: None,
enr_quic4_port: None,
enr_tcp4_port: None, enr_tcp4_port: None,
enr_udp6_port: None, enr_udp6_port: None,
enr_quic6_port: None,
enr_tcp6_port: None, enr_tcp6_port: None,
target_peers: 50, target_peers: 50,
gs_config, gs_config,
@ -320,6 +355,7 @@ impl Default for Config {
disable_peer_scoring: false, disable_peer_scoring: false,
client_version: lighthouse_version::version_with_platform(), client_version: lighthouse_version::version_with_platform(),
disable_discovery: false, disable_discovery: false,
disable_quic_support: false,
upnp_enabled: true, upnp_enabled: true,
network_load: 3, network_load: 3,
private: false, private: false,
@ -417,7 +453,7 @@ pub fn gossipsub_config(
// We use the first 8 bytes of SHA256(topic, data) for content addressing // We use the first 8 bytes of SHA256(topic, data) for content addressing
let fast_gossip_message_id = |message: &gossipsub::RawMessage| { let fast_gossip_message_id = |message: &gossipsub::RawMessage| {
let data = [message.topic.as_str().as_bytes(), &message.data].concat(); let data = [message.topic.as_str().as_bytes(), &message.data].concat();
gossipsub::FastMessageId::from(&Sha256::digest(data)[..8]) gossipsub::FastMessageId::from(&Sha256::digest(&data)[..8])
}; };
fn prefix( fn prefix(
prefix: [u8; 4], prefix: [u8; 4],

View File

@ -17,6 +17,8 @@ use std::path::Path;
use std::str::FromStr; use std::str::FromStr;
use types::{EnrForkId, EthSpec}; use types::{EnrForkId, EthSpec};
use super::enr_ext::{EnrExt, QUIC6_ENR_KEY, QUIC_ENR_KEY};
/// The ENR field specifying the fork id. /// The ENR field specifying the fork id.
pub const ETH2_ENR_KEY: &str = "eth2"; pub const ETH2_ENR_KEY: &str = "eth2";
/// The ENR field specifying the attestation subnet bitfield. /// The ENR field specifying the attestation subnet bitfield.
@ -142,7 +144,7 @@ pub fn build_or_load_enr<T: EthSpec>(
pub fn create_enr_builder_from_config<T: EnrKey>( pub fn create_enr_builder_from_config<T: EnrKey>(
config: &NetworkConfig, config: &NetworkConfig,
enable_tcp: bool, enable_libp2p: bool,
) -> EnrBuilder<T> { ) -> EnrBuilder<T> {
let mut builder = EnrBuilder::new("v4"); let mut builder = EnrBuilder::new("v4");
let (maybe_ipv4_address, maybe_ipv6_address) = &config.enr_address; let (maybe_ipv4_address, maybe_ipv6_address) = &config.enr_address;
@ -163,7 +165,28 @@ pub fn create_enr_builder_from_config<T: EnrKey>(
builder.udp6(udp6_port); builder.udp6(udp6_port);
} }
if enable_tcp { if enable_libp2p {
// Add QUIC fields to the ENR.
// Since QUIC is used as an alternative transport for the libp2p protocols,
// the related fields should only be added when both QUIC and libp2p are enabled
if !config.disable_quic_support {
// If we are listening on ipv4, add the quic ipv4 port.
if let Some(quic4_port) = config
.enr_quic4_port
.or_else(|| config.listen_addrs().v4().map(|v4_addr| v4_addr.quic_port))
{
builder.add_value(QUIC_ENR_KEY, &quic4_port);
}
// If we are listening on ipv6, add the quic ipv6 port.
if let Some(quic6_port) = config
.enr_quic6_port
.or_else(|| config.listen_addrs().v6().map(|v6_addr| v6_addr.quic_port))
{
builder.add_value(QUIC6_ENR_KEY, &quic6_port);
}
}
// 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 .enr_tcp4_port
@ -218,6 +241,9 @@ fn compare_enr(local_enr: &Enr, disk_enr: &Enr) -> bool {
// tcp ports must match // tcp ports must match
&& local_enr.tcp4() == disk_enr.tcp4() && local_enr.tcp4() == disk_enr.tcp4()
&& local_enr.tcp6() == disk_enr.tcp6() && local_enr.tcp6() == disk_enr.tcp6()
// quic ports must match
&& local_enr.quic4() == disk_enr.quic4()
&& local_enr.quic6() == disk_enr.quic6()
// must match on the same fork // must match on the same fork
&& local_enr.get(ETH2_ENR_KEY) == disk_enr.get(ETH2_ENR_KEY) && local_enr.get(ETH2_ENR_KEY) == disk_enr.get(ETH2_ENR_KEY)
// take preference over disk udp port if one is not specified // take preference over disk udp port if one is not specified

View File

@ -6,12 +6,15 @@ use libp2p::core::multiaddr::Protocol;
use libp2p::identity::{ed25519, secp256k1, KeyType, Keypair, PublicKey}; use libp2p::identity::{ed25519, secp256k1, KeyType, Keypair, PublicKey};
use tiny_keccak::{Hasher, Keccak}; use tiny_keccak::{Hasher, Keccak};
pub const QUIC_ENR_KEY: &str = "quic";
pub const QUIC6_ENR_KEY: &str = "quic6";
/// Extend ENR for libp2p types. /// Extend ENR for libp2p types.
pub trait EnrExt { pub trait EnrExt {
/// The libp2p `PeerId` for the record. /// The libp2p `PeerId` for the record.
fn peer_id(&self) -> PeerId; fn peer_id(&self) -> PeerId;
/// Returns a list of multiaddrs if the ENR has an `ip` and either a `tcp` or `udp` key **or** an `ip6` and either a `tcp6` or `udp6`. /// Returns a list of multiaddrs if the ENR has an `ip` and one of [`tcp`,`udp`,`quic`] key **or** an `ip6` and one of [`tcp6`,`udp6`,`quic6`].
/// The vector remains empty if these fields are not defined. /// The vector remains empty if these fields are not defined.
fn multiaddr(&self) -> Vec<Multiaddr>; fn multiaddr(&self) -> Vec<Multiaddr>;
@ -26,6 +29,15 @@ pub trait EnrExt {
/// Returns any multiaddrs that contain the TCP protocol. /// Returns any multiaddrs that contain the TCP protocol.
fn multiaddr_tcp(&self) -> Vec<Multiaddr>; fn multiaddr_tcp(&self) -> Vec<Multiaddr>;
/// Returns any QUIC multiaddrs that are registered in this ENR.
fn multiaddr_quic(&self) -> Vec<Multiaddr>;
/// Returns the quic port if one is set.
fn quic4(&self) -> Option<u16>;
/// Returns the quic6 port if one is set.
fn quic6(&self) -> Option<u16>;
} }
/// Extend ENR CombinedPublicKey for libp2p types. /// Extend ENR CombinedPublicKey for libp2p types.
@ -49,7 +61,17 @@ impl EnrExt for Enr {
self.public_key().as_peer_id() self.public_key().as_peer_id()
} }
/// Returns a list of multiaddrs if the ENR has an `ip` and either a `tcp` or `udp` key **or** an `ip6` and either a `tcp6` or `udp6`. /// Returns the quic port if one is set.
fn quic4(&self) -> Option<u16> {
self.get_decodable(QUIC_ENR_KEY).and_then(Result::ok)
}
/// Returns the quic6 port if one is set.
fn quic6(&self) -> Option<u16> {
self.get_decodable(QUIC6_ENR_KEY).and_then(Result::ok)
}
/// Returns a list of multiaddrs if the ENR has an `ip` and either a `tcp`, `quic` or `udp` key **or** an `ip6` and either a `tcp6` `quic6` or `udp6`.
/// The vector remains empty if these fields are not defined. /// The vector remains empty if these fields are not defined.
fn multiaddr(&self) -> Vec<Multiaddr> { fn multiaddr(&self) -> Vec<Multiaddr> {
let mut multiaddrs: Vec<Multiaddr> = Vec::new(); let mut multiaddrs: Vec<Multiaddr> = Vec::new();
@ -59,6 +81,12 @@ impl EnrExt for Enr {
multiaddr.push(Protocol::Udp(udp)); multiaddr.push(Protocol::Udp(udp));
multiaddrs.push(multiaddr); multiaddrs.push(multiaddr);
} }
if let Some(quic) = self.quic4() {
let mut multiaddr: Multiaddr = ip.into();
multiaddr.push(Protocol::Udp(quic));
multiaddr.push(Protocol::QuicV1);
multiaddrs.push(multiaddr);
}
if let Some(tcp) = self.tcp4() { if let Some(tcp) = self.tcp4() {
let mut multiaddr: Multiaddr = ip.into(); let mut multiaddr: Multiaddr = ip.into();
@ -73,6 +101,13 @@ impl EnrExt for Enr {
multiaddrs.push(multiaddr); multiaddrs.push(multiaddr);
} }
if let Some(quic6) = self.quic6() {
let mut multiaddr: Multiaddr = ip6.into();
multiaddr.push(Protocol::Udp(quic6));
multiaddr.push(Protocol::QuicV1);
multiaddrs.push(multiaddr);
}
if let Some(tcp6) = self.tcp6() { if let Some(tcp6) = self.tcp6() {
let mut multiaddr: Multiaddr = ip6.into(); let mut multiaddr: Multiaddr = ip6.into();
multiaddr.push(Protocol::Tcp(tcp6)); multiaddr.push(Protocol::Tcp(tcp6));
@ -174,8 +209,30 @@ impl EnrExt for Enr {
multiaddrs multiaddrs
} }
/// Returns a list of multiaddrs if the ENR has an `ip` and a `quic` key **or** an `ip6` and a `quic6`.
fn multiaddr_quic(&self) -> Vec<Multiaddr> {
let mut multiaddrs: Vec<Multiaddr> = Vec::new();
if let Some(quic_port) = self.quic4() {
if let Some(ip) = self.ip4() {
let mut multiaddr: Multiaddr = ip.into();
multiaddr.push(Protocol::Udp(quic_port));
multiaddr.push(Protocol::QuicV1);
multiaddrs.push(multiaddr);
}
}
if let Some(quic6_port) = self.quic6() {
if let Some(ip6) = self.ip6() {
let mut multiaddr: Multiaddr = ip6.into();
multiaddr.push(Protocol::Udp(quic6_port));
multiaddr.push(Protocol::QuicV1);
multiaddrs.push(multiaddr);
}
}
multiaddrs
}
/// Returns a list of multiaddrs if the ENR has an `ip` and either a `tcp` or `udp` key **or** an `ip6` and either a `tcp6` or `udp6`. /// Returns a list of multiaddrs if the ENR has an `ip` and either a `tcp` or `udp` key **or** an `ip6` and either a `tcp6` or `udp6`.
/// The vector remains empty if these fields are not defined.
fn multiaddr_tcp(&self) -> Vec<Multiaddr> { fn multiaddr_tcp(&self) -> Vec<Multiaddr> {
let mut multiaddrs: Vec<Multiaddr> = Vec::new(); let mut multiaddrs: Vec<Multiaddr> = Vec::new();
if let Some(ip) = self.ip4() { if let Some(ip) = self.ip4() {

View File

@ -21,7 +21,6 @@ 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::{
@ -75,7 +74,7 @@ const DURATION_DIFFERENCE: Duration = Duration::from_millis(1);
/// of the peer if it is specified. /// of the peer if it is specified.
#[derive(Debug)] #[derive(Debug)]
pub struct DiscoveredPeers { pub struct DiscoveredPeers {
pub peers: HashMap<PeerId, Option<Instant>>, pub peers: HashMap<Enr, Option<Instant>>,
} }
#[derive(Clone, PartialEq)] #[derive(Clone, PartialEq)]
@ -208,7 +207,8 @@ impl<TSpec: EthSpec> Discovery<TSpec> {
let local_node_id = local_enr.node_id(); let local_node_id = local_enr.node_id();
info!(log, "ENR Initialised"; "enr" => local_enr.to_base64(), "seq" => local_enr.seq(), "id"=> %local_enr.node_id(), info!(log, "ENR Initialised"; "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() "ip4" => ?local_enr.ip4(), "udp4"=> ?local_enr.udp4(), "tcp4" => ?local_enr.tcp4(), "tcp6" => ?local_enr.tcp6(), "udp6" => ?local_enr.udp6(),
"quic4" => ?local_enr.quic4(), "quic6" => ?local_enr.quic6()
); );
// convert the keypair into an ENR key // convert the keypair into an ENR key
@ -230,7 +230,8 @@ impl<TSpec: EthSpec> Discovery<TSpec> {
"peer_id" => %bootnode_enr.peer_id(), "peer_id" => %bootnode_enr.peer_id(),
"ip" => ?bootnode_enr.ip4(), "ip" => ?bootnode_enr.ip4(),
"udp" => ?bootnode_enr.udp4(), "udp" => ?bootnode_enr.udp4(),
"tcp" => ?bootnode_enr.tcp4() "tcp" => ?bootnode_enr.tcp4(),
"quic" => ?bootnode_enr.quic4()
); );
let repr = bootnode_enr.to_string(); let repr = bootnode_enr.to_string();
let _ = discv5.add_enr(bootnode_enr).map_err(|e| { let _ = discv5.add_enr(bootnode_enr).map_err(|e| {
@ -281,7 +282,8 @@ impl<TSpec: EthSpec> Discovery<TSpec> {
"peer_id" => %enr.peer_id(), "peer_id" => %enr.peer_id(),
"ip" => ?enr.ip4(), "ip" => ?enr.ip4(),
"udp" => ?enr.udp4(), "udp" => ?enr.udp4(),
"tcp" => ?enr.tcp4() "tcp" => ?enr.tcp4(),
"quic" => ?enr.quic4()
); );
let _ = discv5.add_enr(enr).map_err(|e| { let _ = discv5.add_enr(enr).map_err(|e| {
error!( error!(
@ -383,20 +385,6 @@ impl<TSpec: EthSpec> Discovery<TSpec> {
self.discv5.table_entries_enr() self.discv5.table_entries_enr()
} }
/// Returns the ENR of a known peer if it exists.
pub fn enr_of_peer(&mut self, peer_id: &PeerId) -> Option<Enr> {
// first search the local cache
if let Some(enr) = self.cached_enrs.get(peer_id) {
return Some(enr.clone());
}
// not in the local cache, look in the routing table
if let Ok(node_id) = enr_ext::peer_id_to_node_id(peer_id) {
self.discv5.find_enr(&node_id)
} else {
None
}
}
/// Updates the local ENR TCP port. /// Updates the local ENR TCP port.
/// There currently isn't a case to update the address here. We opt for discovery to /// There currently isn't a case to update the address here. We opt for discovery to
/// automatically update the external address. /// automatically update the external address.
@ -414,6 +402,23 @@ impl<TSpec: EthSpec> Discovery<TSpec> {
Ok(()) Ok(())
} }
// TODO: Group these functions here once the ENR is shared across discv5 and lighthouse and
// Lighthouse can modify the ENR directly.
// This currently doesn't support ipv6. All of these functions should be removed and
// addressed properly in the following issue.
// https://github.com/sigp/lighthouse/issues/4706
pub fn update_enr_quic_port(&mut self, port: u16) -> Result<(), String> {
self.discv5
.enr_insert("quic", &port)
.map_err(|e| format!("{:?}", e))?;
// replace the global version
*self.network_globals.local_enr.write() = self.discv5.local_enr();
// persist modified enr to disk
enr::save_enr_to_disk(Path::new(&self.enr_dir), &self.local_enr(), &self.log);
Ok(())
}
/// Updates the local ENR UDP socket. /// Updates the local ENR UDP socket.
/// ///
/// This is with caution. Discovery should automatically maintain this. This should only be /// This is with caution. Discovery should automatically maintain this. This should only be
@ -733,23 +738,6 @@ impl<TSpec: EthSpec> Discovery<TSpec> {
target_peers: usize, target_peers: usize,
additional_predicate: impl Fn(&Enr) -> bool + Send + 'static, additional_predicate: impl Fn(&Enr) -> bool + Send + 'static,
) { ) {
// Make sure there are subnet queries included
let contains_queries = match &query {
QueryType::Subnet(queries) => !queries.is_empty(),
QueryType::FindPeers => true,
};
if !contains_queries {
debug!(
self.log,
"No subnets included in this request. Skipping discovery request."
);
return;
}
// Generate a random target node id.
let random_node = NodeId::random();
let enr_fork_id = match self.local_enr().eth2() { let enr_fork_id = match self.local_enr().eth2() {
Ok(v) => v, Ok(v) => v,
Err(e) => { Err(e) => {
@ -773,7 +761,8 @@ impl<TSpec: EthSpec> Discovery<TSpec> {
// Build the future // Build the future
let query_future = self let query_future = self
.discv5 .discv5
.find_node_predicate(random_node, predicate, target_peers) // Generate a random target node id.
.find_node_predicate(NodeId::random(), predicate, target_peers)
.map(|v| QueryResult { .map(|v| QueryResult {
query_type: query, query_type: query,
result: v, result: v,
@ -787,7 +776,7 @@ impl<TSpec: EthSpec> Discovery<TSpec> {
fn process_completed_queries( fn process_completed_queries(
&mut self, &mut self,
query: QueryResult, query: QueryResult,
) -> Option<HashMap<PeerId, Option<Instant>>> { ) -> Option<HashMap<Enr, Option<Instant>>> {
match query.query_type { match query.query_type {
QueryType::FindPeers => { QueryType::FindPeers => {
self.find_peer_active = false; self.find_peer_active = false;
@ -797,12 +786,14 @@ impl<TSpec: EthSpec> Discovery<TSpec> {
} }
Ok(r) => { Ok(r) => {
debug!(self.log, "Discovery query completed"; "peers_found" => r.len()); debug!(self.log, "Discovery query completed"; "peers_found" => r.len());
let mut results: HashMap<_, Option<Instant>> = HashMap::new(); let results = r
r.iter().for_each(|enr| { .into_iter()
// cache the found ENR's .map(|enr| {
self.cached_enrs.put(enr.peer_id(), enr.clone()); // cache the found ENR's
results.insert(enr.peer_id(), None); self.cached_enrs.put(enr.peer_id(), enr.clone());
}); (enr, None)
})
.collect();
return Some(results); return Some(results);
} }
Err(e) => { Err(e) => {
@ -850,17 +841,17 @@ impl<TSpec: EthSpec> Discovery<TSpec> {
let subnet_predicate = let subnet_predicate =
subnet_predicate::<TSpec>(vec![query.subnet], &self.log); subnet_predicate::<TSpec>(vec![query.subnet], &self.log);
r.iter() r.clone()
.into_iter()
.filter(|enr| subnet_predicate(enr)) .filter(|enr| subnet_predicate(enr))
.map(|enr| enr.peer_id()) .for_each(|enr| {
.for_each(|peer_id| {
if let Some(v) = metrics::get_int_counter( if let Some(v) = metrics::get_int_counter(
&metrics::SUBNET_PEERS_FOUND, &metrics::SUBNET_PEERS_FOUND,
&[query_str], &[query_str],
) { ) {
v.inc(); v.inc();
} }
let other_min_ttl = mapped_results.get_mut(&peer_id); let other_min_ttl = mapped_results.get_mut(&enr);
// map peer IDs to the min_ttl furthest in the future // map peer IDs to the min_ttl furthest in the future
match (query.min_ttl, other_min_ttl) { match (query.min_ttl, other_min_ttl) {
@ -878,15 +869,11 @@ impl<TSpec: EthSpec> Discovery<TSpec> {
} }
// update the mapping if we have a specified min_ttl // update the mapping if we have a specified min_ttl
(Some(min_ttl), Some(None)) => { (Some(min_ttl), Some(None)) => {
mapped_results.insert(peer_id, Some(min_ttl)); mapped_results.insert(enr, Some(min_ttl));
} }
// first seen min_ttl for this enr // first seen min_ttl for this enr
(Some(min_ttl), None) => { (min_ttl, None) => {
mapped_results.insert(peer_id, Some(min_ttl)); mapped_results.insert(enr, min_ttl);
}
// first seen min_ttl for this enr
(None, None) => {
mapped_results.insert(peer_id, None);
} }
(None, Some(Some(_))) => {} // Don't replace the existing specific min_ttl (None, Some(Some(_))) => {} // Don't replace the existing specific min_ttl
(None, Some(None)) => {} // No-op because this is a duplicate (None, Some(None)) => {} // No-op because this is a duplicate
@ -910,7 +897,7 @@ impl<TSpec: EthSpec> Discovery<TSpec> {
} }
/// Drives the queries returning any results from completed queries. /// Drives the queries returning any results from completed queries.
fn poll_queries(&mut self, cx: &mut Context) -> Option<HashMap<PeerId, Option<Instant>>> { fn poll_queries(&mut self, cx: &mut Context) -> Option<HashMap<Enr, Option<Instant>>> {
while let Poll::Ready(Some(query_result)) = self.active_queries.poll_next_unpin(cx) { while let Poll::Ready(Some(query_result)) = self.active_queries.poll_next_unpin(cx) {
let result = self.process_completed_queries(query_result); let result = self.process_completed_queries(query_result);
if result.is_some() { if result.is_some() {
@ -957,23 +944,6 @@ impl<TSpec: EthSpec> NetworkBehaviour for Discovery<TSpec> {
) { ) {
} }
fn handle_pending_outbound_connection(
&mut self,
_connection_id: ConnectionId,
maybe_peer: Option<PeerId>,
_addresses: &[Multiaddr],
_effective_role: libp2p::core::Endpoint,
) -> Result<Vec<Multiaddr>, libp2p::swarm::ConnectionDenied> {
if let Some(enr) = maybe_peer.and_then(|peer_id| self.enr_of_peer(&peer_id)) {
// ENR's may have multiple Multiaddrs. The multi-addr associated with the UDP
// port is removed, which is assumed to be associated with the discv5 protocol (and
// therefore irrelevant for other libp2p components).
Ok(enr.multiaddr_tcp())
} else {
Ok(vec![])
}
}
// Main execution loop to drive the behaviour // Main execution loop to drive the behaviour
fn poll( fn poll(
&mut self, &mut self,
@ -1047,25 +1017,8 @@ impl<TSpec: EthSpec> NetworkBehaviour for Discovery<TSpec> {
// update network globals // update network globals
*self.network_globals.local_enr.write() = enr; *self.network_globals.local_enr.write() = enr;
// A new UDP socket has been detected. // A new UDP socket has been detected.
// Build a multiaddr to report to libp2p // NOTE: We assume libp2p itself can keep track of IP changes and we do
let addr = match socket_addr.ip() { // not inform it about IP changes found via discovery.
IpAddr::V4(v4_addr) => {
self.network_globals.listen_port_tcp4().map(|tcp4_port| {
Multiaddr::from(v4_addr).with(Protocol::Tcp(tcp4_port))
})
}
IpAddr::V6(v6_addr) => {
self.network_globals.listen_port_tcp6().map(|tcp6_port| {
Multiaddr::from(v6_addr).with(Protocol::Tcp(tcp6_port))
})
}
};
if let Some(address) = addr {
// NOTE: This doesn't actually track the external TCP port. More sophisticated NAT handling
// should handle this.
return Poll::Ready(ToSwarm::NewExternalAddrCandidate(address));
}
} }
Discv5Event::EnrAdded { .. } Discv5Event::EnrAdded { .. }
| Discv5Event::TalkRequest(_) | Discv5Event::TalkRequest(_)
@ -1152,8 +1105,6 @@ mod tests {
let log = build_log(slog::Level::Debug, false); let log = build_log(slog::Level::Debug, false);
let globals = NetworkGlobals::new( let globals = NetworkGlobals::new(
enr, enr,
Some(9000),
None,
MetaData::V2(MetaDataV2 { MetaData::V2(MetaDataV2 {
seq_number: 0, seq_number: 0,
attnets: Default::default(), attnets: Default::default(),
@ -1261,6 +1212,6 @@ mod tests {
assert_eq!(results.len(), 2); assert_eq!(results.len(), 2);
// when a peer belongs to multiple subnet ids, we use the highest ttl. // when a peer belongs to multiple subnet ids, we use the highest ttl.
assert_eq!(results.get(&enr1.peer_id()).unwrap(), &instant1); assert_eq!(results.get(&enr1).unwrap(), &instant1);
} }
} }

View File

@ -6,14 +6,23 @@ use serde::{Deserialize, Serialize};
/// A listening address composed by an Ip, an UDP port and a TCP port. /// A listening address composed by an Ip, an UDP port and a TCP port.
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ListenAddr<Ip> { pub struct ListenAddr<Ip> {
/// The IP address we will listen on.
pub addr: Ip, pub addr: Ip,
pub udp_port: u16, /// The UDP port that discovery will listen on.
pub disc_port: u16,
/// The UDP port that QUIC will listen on.
pub quic_port: u16,
/// The TCP port that libp2p will listen on.
pub tcp_port: u16, pub tcp_port: u16,
} }
impl<Ip: Into<IpAddr> + Clone> ListenAddr<Ip> { impl<Ip: Into<IpAddr> + Clone> ListenAddr<Ip> {
pub fn udp_socket_addr(&self) -> SocketAddr { pub fn discovery_socket_addr(&self) -> SocketAddr {
(self.addr.clone().into(), self.udp_port).into() (self.addr.clone().into(), self.disc_port).into()
}
pub fn quic_socket_addr(&self) -> SocketAddr {
(self.addr.clone().into(), self.quic_port).into()
} }
pub fn tcp_socket_addr(&self) -> SocketAddr { pub fn tcp_socket_addr(&self) -> SocketAddr {
@ -46,22 +55,41 @@ impl ListenAddress {
} }
} }
/// Returns the TCP addresses. /// Returns the addresses the Swarm will listen on, given the setup.
pub fn tcp_addresses(&self) -> impl Iterator<Item = Multiaddr> + '_ { pub fn libp2p_addresses(&self) -> impl Iterator<Item = Multiaddr> {
let v4_multiaddr = self let v4_tcp_multiaddr = self
.v4() .v4()
.map(|v4_addr| Multiaddr::from(v4_addr.addr).with(Protocol::Tcp(v4_addr.tcp_port))); .map(|v4_addr| Multiaddr::from(v4_addr.addr).with(Protocol::Tcp(v4_addr.tcp_port)));
let v6_multiaddr = self
let v4_quic_multiaddr = self.v4().map(|v4_addr| {
Multiaddr::from(v4_addr.addr)
.with(Protocol::Udp(v4_addr.quic_port))
.with(Protocol::QuicV1)
});
let v6_quic_multiaddr = self.v6().map(|v6_addr| {
Multiaddr::from(v6_addr.addr)
.with(Protocol::Udp(v6_addr.quic_port))
.with(Protocol::QuicV1)
});
let v6_tcp_multiaddr = self
.v6() .v6()
.map(|v6_addr| Multiaddr::from(v6_addr.addr).with(Protocol::Tcp(v6_addr.tcp_port))); .map(|v6_addr| Multiaddr::from(v6_addr.addr).with(Protocol::Tcp(v6_addr.tcp_port)));
v4_multiaddr.into_iter().chain(v6_multiaddr)
v4_tcp_multiaddr
.into_iter()
.chain(v4_quic_multiaddr)
.chain(v6_quic_multiaddr)
.chain(v6_tcp_multiaddr)
} }
#[cfg(test)] #[cfg(test)]
pub fn unused_v4_ports() -> Self { pub fn unused_v4_ports() -> Self {
ListenAddress::V4(ListenAddr { ListenAddress::V4(ListenAddr {
addr: Ipv4Addr::UNSPECIFIED, addr: Ipv4Addr::UNSPECIFIED,
udp_port: unused_port::unused_udp4_port().unwrap(), disc_port: unused_port::unused_udp4_port().unwrap(),
quic_port: unused_port::unused_udp4_port().unwrap(),
tcp_port: unused_port::unused_tcp4_port().unwrap(), tcp_port: unused_port::unused_tcp4_port().unwrap(),
}) })
} }
@ -70,7 +98,8 @@ impl ListenAddress {
pub fn unused_v6_ports() -> Self { pub fn unused_v6_ports() -> Self {
ListenAddress::V6(ListenAddr { ListenAddress::V6(ListenAddr {
addr: Ipv6Addr::UNSPECIFIED, addr: Ipv6Addr::UNSPECIFIED,
udp_port: unused_port::unused_udp6_port().unwrap(), disc_port: unused_port::unused_udp6_port().unwrap(),
quic_port: unused_port::unused_udp6_port().unwrap(),
tcp_port: unused_port::unused_tcp6_port().unwrap(), tcp_port: unused_port::unused_tcp6_port().unwrap(),
}) })
} }
@ -84,12 +113,14 @@ impl slog::KV for ListenAddress {
) -> slog::Result { ) -> slog::Result {
if let Some(v4_addr) = self.v4() { if let Some(v4_addr) = self.v4() {
serializer.emit_arguments("ip4_address", &format_args!("{}", v4_addr.addr))?; serializer.emit_arguments("ip4_address", &format_args!("{}", v4_addr.addr))?;
serializer.emit_u16("udp4_port", v4_addr.udp_port)?; serializer.emit_u16("disc4_port", v4_addr.disc_port)?;
serializer.emit_u16("quic4_port", v4_addr.quic_port)?;
serializer.emit_u16("tcp4_port", v4_addr.tcp_port)?; serializer.emit_u16("tcp4_port", v4_addr.tcp_port)?;
} }
if let Some(v6_addr) = self.v6() { if let Some(v6_addr) = self.v6() {
serializer.emit_arguments("ip6_address", &format_args!("{}", v6_addr.addr))?; serializer.emit_arguments("ip6_address", &format_args!("{}", v6_addr.addr))?;
serializer.emit_u16("udp6_port", v6_addr.udp_port)?; serializer.emit_u16("disc6_port", v6_addr.disc_port)?;
serializer.emit_u16("quic6_port", v6_addr.quic_port)?;
serializer.emit_u16("tcp6_port", v6_addr.tcp_port)?; serializer.emit_u16("tcp6_port", v6_addr.tcp_port)?;
} }
slog::Result::Ok(()) slog::Result::Ok(())

View File

@ -14,6 +14,16 @@ lazy_static! {
"Count of libp2p peers currently connected" "Count of libp2p peers currently connected"
); );
pub static ref TCP_PEERS_CONNECTED: Result<IntGauge> = try_create_int_gauge(
"libp2p_tcp_peers",
"Count of libp2p peers currently connected via TCP"
);
pub static ref QUIC_PEERS_CONNECTED: Result<IntGauge> = try_create_int_gauge(
"libp2p_quic_peers",
"Count of libp2p peers currently connected via QUIC"
);
pub static ref PEER_CONNECT_EVENT_COUNT: Result<IntCounter> = try_create_int_counter( pub static ref PEER_CONNECT_EVENT_COUNT: Result<IntCounter> = try_create_int_counter(
"libp2p_peer_connect_event_total", "libp2p_peer_connect_event_total",
"Count of libp2p peer connect events (not the current number of connected peers)" "Count of libp2p peer connect events (not the current number of connected peers)"

View File

@ -1,5 +1,6 @@
//! Implementation of Lighthouse's peer management system. //! Implementation of Lighthouse's peer management system.
use crate::discovery::enr_ext::EnrExt;
use crate::rpc::{GoodbyeReason, MetaData, Protocol, RPCError, RPCResponseErrorCode}; use crate::rpc::{GoodbyeReason, MetaData, Protocol, RPCError, RPCResponseErrorCode};
use crate::service::TARGET_SUBNET_PEERS; use crate::service::TARGET_SUBNET_PEERS;
use crate::{error, metrics, Gossipsub}; use crate::{error, metrics, Gossipsub};
@ -13,7 +14,6 @@ use peerdb::{client::ClientKind, BanOperation, BanResult, ScoreUpdateResult};
use rand::seq::SliceRandom; use rand::seq::SliceRandom;
use slog::{debug, error, trace, warn}; use slog::{debug, error, trace, warn};
use smallvec::SmallVec; use smallvec::SmallVec;
use std::collections::BTreeMap;
use std::{ use std::{
sync::Arc, sync::Arc,
time::{Duration, Instant}, time::{Duration, Instant},
@ -78,7 +78,7 @@ pub struct PeerManager<TSpec: EthSpec> {
/// The target number of peers we would like to connect to. /// The target number of peers we would like to connect to.
target_peers: usize, target_peers: usize,
/// Peers queued to be dialed. /// Peers queued to be dialed.
peers_to_dial: BTreeMap<PeerId, Option<Enr>>, peers_to_dial: Vec<Enr>,
/// The number of temporarily banned peers. This is used to prevent instantaneous /// The number of temporarily banned peers. This is used to prevent instantaneous
/// reconnection. /// reconnection.
// NOTE: This just prevents re-connections. The state of the peer is otherwise unaffected. A // NOTE: This just prevents re-connections. The state of the peer is otherwise unaffected. A
@ -312,16 +312,12 @@ impl<TSpec: EthSpec> PeerManager<TSpec> {
/// Peers that have been returned by discovery requests that are suitable for dialing are /// Peers that have been returned by discovery requests that are suitable for dialing are
/// returned here. /// returned here.
/// ///
/// NOTE: By dialing `PeerId`s and not multiaddrs, libp2p requests the multiaddr associated /// This function decides whether or not to dial these peers.
/// with a new `PeerId` which involves a discovery routing table lookup. We could dial the
/// multiaddr here, however this could relate to duplicate PeerId's etc. If the lookup
/// proves resource constraining, we should switch to multiaddr dialling here.
#[allow(clippy::mutable_key_type)] #[allow(clippy::mutable_key_type)]
pub fn peers_discovered(&mut self, results: HashMap<PeerId, Option<Instant>>) -> Vec<PeerId> { pub fn peers_discovered(&mut self, results: HashMap<Enr, Option<Instant>>) {
let mut to_dial_peers = Vec::with_capacity(4); let mut to_dial_peers = 0;
let connected_or_dialing = self.network_globals.connected_or_dialing_peers(); let connected_or_dialing = self.network_globals.connected_or_dialing_peers();
for (peer_id, min_ttl) in results { for (enr, min_ttl) in results {
// There are two conditions in deciding whether to dial this peer. // There are two conditions in deciding whether to dial this peer.
// 1. If we are less than our max connections. Discovery queries are executed to reach // 1. If we are less than our max connections. Discovery queries are executed to reach
// our target peers, so its fine to dial up to our max peers (which will get pruned // our target peers, so its fine to dial up to our max peers (which will get pruned
@ -330,10 +326,8 @@ impl<TSpec: EthSpec> PeerManager<TSpec> {
// considered a priority. We have pre-allocated some extra priority slots for these // considered a priority. We have pre-allocated some extra priority slots for these
// peers as specified by PRIORITY_PEER_EXCESS. Therefore we dial these peers, even // peers as specified by PRIORITY_PEER_EXCESS. Therefore we dial these peers, even
// if we are already at our max_peer limit. // if we are already at our max_peer limit.
if (min_ttl.is_some() if min_ttl.is_some() && connected_or_dialing + to_dial_peers < self.max_priority_peers()
&& connected_or_dialing + to_dial_peers.len() < self.max_priority_peers() || connected_or_dialing + to_dial_peers < self.max_peers()
|| connected_or_dialing + to_dial_peers.len() < self.max_peers())
&& self.network_globals.peers.read().should_dial(&peer_id)
{ {
// This should be updated with the peer dialing. In fact created once the peer is // This should be updated with the peer dialing. In fact created once the peer is
// dialed // dialed
@ -341,16 +335,16 @@ impl<TSpec: EthSpec> PeerManager<TSpec> {
self.network_globals self.network_globals
.peers .peers
.write() .write()
.update_min_ttl(&peer_id, min_ttl); .update_min_ttl(&enr.peer_id(), min_ttl);
} }
to_dial_peers.push(peer_id); debug!(self.log, "Dialing discovered peer"; "peer_id" => %enr.peer_id());
self.dial_peer(enr);
to_dial_peers += 1;
} }
} }
// Queue another discovery if we need to // Queue another discovery if we need to
self.maintain_peer_count(to_dial_peers.len()); self.maintain_peer_count(to_dial_peers);
to_dial_peers
} }
/// A STATUS message has been received from a peer. This resets the status timer. /// A STATUS message has been received from a peer. This resets the status timer.
@ -406,9 +400,16 @@ impl<TSpec: EthSpec> PeerManager<TSpec> {
/* Notifications from the Swarm */ /* Notifications from the Swarm */
// A peer is being dialed. /// A peer is being dialed.
pub fn dial_peer(&mut self, peer_id: &PeerId, enr: Option<Enr>) { pub fn dial_peer(&mut self, peer: Enr) {
self.peers_to_dial.insert(*peer_id, enr); if self
.network_globals
.peers
.read()
.should_dial(&peer.peer_id())
{
self.peers_to_dial.push(peer);
}
} }
/// Reports if a peer is banned or not. /// Reports if a peer is banned or not.
@ -2208,7 +2209,7 @@ mod tests {
} }
impl Arbitrary for PeerCondition { impl Arbitrary for PeerCondition {
fn arbitrary<G: Gen>(g: &mut G) -> Self { fn arbitrary(g: &mut Gen) -> Self {
let attestation_net_bitfield = { let attestation_net_bitfield = {
let len = <E as EthSpec>::SubnetBitfieldLength::to_usize(); let len = <E as EthSpec>::SubnetBitfieldLength::to_usize();
let mut bitfield = Vec::with_capacity(len); let mut bitfield = Vec::with_capacity(len);

View File

@ -3,7 +3,7 @@
use std::task::{Context, Poll}; use std::task::{Context, Poll};
use futures::StreamExt; use futures::StreamExt;
use libp2p::core::ConnectedPoint; use libp2p::core::{multiaddr, ConnectedPoint};
use libp2p::identity::PeerId; 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};
@ -12,6 +12,7 @@ use libp2p::swarm::{ConnectionId, NetworkBehaviour, PollParameters, ToSwarm};
use slog::{debug, error}; use slog::{debug, error};
use types::EthSpec; use types::EthSpec;
use crate::discovery::enr_ext::EnrExt;
use crate::rpc::GoodbyeReason; use crate::rpc::GoodbyeReason;
use crate::types::SyncState; use crate::types::SyncState;
use crate::{metrics, ClearDialError}; use crate::{metrics, ClearDialError};
@ -95,11 +96,23 @@ impl<TSpec: EthSpec> NetworkBehaviour for PeerManager<TSpec> {
self.events.shrink_to_fit(); self.events.shrink_to_fit();
} }
if let Some((peer_id, maybe_enr)) = self.peers_to_dial.pop_first() { if let Some(enr) = self.peers_to_dial.pop() {
self.inject_peer_connection(&peer_id, ConnectingType::Dialing, maybe_enr); let peer_id = enr.peer_id();
self.inject_peer_connection(&peer_id, ConnectingType::Dialing, Some(enr.clone()));
let quic_multiaddrs = enr.multiaddr_quic();
if !quic_multiaddrs.is_empty() {
debug!(self.log, "Dialing QUIC supported peer"; "peer_id"=> %peer_id, "quic_multiaddrs" => ?quic_multiaddrs);
}
// Prioritize Quic connections over Tcp ones.
let multiaddrs = quic_multiaddrs
.into_iter()
.chain(enr.multiaddr_tcp())
.collect();
return Poll::Ready(ToSwarm::Dial { return Poll::Ready(ToSwarm::Dial {
opts: DialOpts::peer_id(peer_id) opts: DialOpts::peer_id(peer_id)
.condition(PeerCondition::Disconnected) .condition(PeerCondition::Disconnected)
.addresses(multiaddrs)
.build(), .build(),
}); });
} }
@ -124,9 +137,11 @@ impl<TSpec: EthSpec> NetworkBehaviour for PeerManager<TSpec> {
} }
FromSwarm::ConnectionClosed(ConnectionClosed { FromSwarm::ConnectionClosed(ConnectionClosed {
peer_id, peer_id,
endpoint,
remaining_established, remaining_established,
.. ..
}) => self.on_connection_closed(peer_id, remaining_established), }) => self.on_connection_closed(peer_id, endpoint, remaining_established),
FromSwarm::DialFailure(DialFailure { FromSwarm::DialFailure(DialFailure {
peer_id, peer_id,
error, error,
@ -184,7 +199,11 @@ impl<TSpec: EthSpec> PeerManager<TSpec> {
endpoint: &ConnectedPoint, endpoint: &ConnectedPoint,
other_established: usize, other_established: usize,
) { ) {
debug!(self.log, "Connection established"; "peer_id" => %peer_id, "connection" => ?endpoint.to_endpoint()); debug!(self.log, "Connection established"; "peer_id" => %peer_id,
"multiaddr" => %endpoint.get_remote_address(),
"connection" => ?endpoint.to_endpoint()
);
if other_established == 0 { if other_established == 0 {
self.events.push(PeerManagerEvent::MetaData(peer_id)); self.events.push(PeerManagerEvent::MetaData(peer_id));
} }
@ -194,6 +213,34 @@ impl<TSpec: EthSpec> PeerManager<TSpec> {
metrics::check_nat(); metrics::check_nat();
} }
// increment prometheus metrics
if self.metrics_enabled {
let remote_addr = match endpoint {
ConnectedPoint::Dialer { address, .. } => address,
ConnectedPoint::Listener { send_back_addr, .. } => send_back_addr,
};
match remote_addr.iter().find(|proto| {
matches!(
proto,
multiaddr::Protocol::QuicV1 | multiaddr::Protocol::Tcp(_)
)
}) {
Some(multiaddr::Protocol::QuicV1) => {
metrics::inc_gauge(&metrics::QUIC_PEERS_CONNECTED);
}
Some(multiaddr::Protocol::Tcp(_)) => {
metrics::inc_gauge(&metrics::TCP_PEERS_CONNECTED);
}
Some(_) => unreachable!(),
None => {
error!(self.log, "Connection established via unknown transport"; "addr" => %remote_addr)
}
};
self.update_connected_peer_metrics();
metrics::inc_counter(&metrics::PEER_CONNECT_EVENT_COUNT);
}
// Check to make sure the peer is not supposed to be banned // Check to make sure the peer is not supposed to be banned
match self.ban_status(&peer_id) { match self.ban_status(&peer_id) {
// TODO: directly emit the ban event? // TODO: directly emit the ban event?
@ -245,14 +292,15 @@ impl<TSpec: EthSpec> PeerManager<TSpec> {
self.events self.events
.push(PeerManagerEvent::PeerConnectedOutgoing(peer_id)); .push(PeerManagerEvent::PeerConnectedOutgoing(peer_id));
} }
} };
// increment prometheus metrics
self.update_connected_peer_metrics();
metrics::inc_counter(&metrics::PEER_CONNECT_EVENT_COUNT);
} }
fn on_connection_closed(&mut self, peer_id: PeerId, remaining_established: usize) { fn on_connection_closed(
&mut self,
peer_id: PeerId,
endpoint: &ConnectedPoint,
remaining_established: usize,
) {
if remaining_established > 0 { if remaining_established > 0 {
return; return;
} }
@ -278,9 +326,31 @@ 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 {
ConnectedPoint::Listener { send_back_addr, .. } => send_back_addr,
ConnectedPoint::Dialer { address, .. } => address,
};
// Update the prometheus metrics // Update the prometheus metrics
self.update_connected_peer_metrics(); if self.metrics_enabled {
metrics::inc_counter(&metrics::PEER_DISCONNECT_EVENT_COUNT); match remote_addr.iter().find(|proto| {
matches!(
proto,
multiaddr::Protocol::QuicV1 | multiaddr::Protocol::Tcp(_)
)
}) {
Some(multiaddr::Protocol::QuicV1) => {
metrics::dec_gauge(&metrics::QUIC_PEERS_CONNECTED);
}
Some(multiaddr::Protocol::Tcp(_)) => {
metrics::dec_gauge(&metrics::TCP_PEERS_CONNECTED);
}
// If it's an unknown protocol we already logged when connection was established.
_ => {}
};
self.update_connected_peer_metrics();
metrics::inc_counter(&metrics::PEER_DISCONNECT_EVENT_COUNT);
}
} }
/// A dial attempt has failed. /// A dial attempt has failed.

View File

@ -1,16 +1,11 @@
use crate::{ use crate::{metrics, multiaddr::Multiaddr, types::Subnet, Enr, Gossipsub, PeerId};
metrics,
multiaddr::{Multiaddr, Protocol},
types::Subnet,
Enr, Gossipsub, PeerId,
};
use peer_info::{ConnectionDirection, PeerConnectionStatus, PeerInfo}; 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::cmp::Ordering;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::net::{IpAddr, SocketAddr}; use std::net::IpAddr;
use std::time::Instant; use std::time::Instant;
use sync_status::SyncStatus; use sync_status::SyncStatus;
use types::EthSpec; use types::EthSpec;
@ -764,28 +759,10 @@ impl<TSpec: EthSpec> PeerDB<TSpec> {
| PeerConnectionStatus::Dialing { .. } => {} | PeerConnectionStatus::Dialing { .. } => {}
} }
// Add the seen ip address and port to the peer's info
let socket_addr = match seen_address.iter().fold(
(None, None),
|(found_ip, found_port), protocol| match protocol {
Protocol::Ip4(ip) => (Some(ip.into()), found_port),
Protocol::Ip6(ip) => (Some(ip.into()), found_port),
Protocol::Tcp(port) => (found_ip, Some(port)),
_ => (found_ip, found_port),
},
) {
(Some(ip), Some(port)) => Some(SocketAddr::new(ip, port)),
(Some(_ip), None) => {
crit!(self.log, "Connected peer has an IP but no TCP port"; "peer_id" => %peer_id);
None
}
_ => None,
};
// Update the connection state // Update the connection state
match direction { match direction {
ConnectionDirection::Incoming => info.connect_ingoing(socket_addr), ConnectionDirection::Incoming => info.connect_ingoing(Some(seen_address)),
ConnectionDirection::Outgoing => info.connect_outgoing(socket_addr), ConnectionDirection::Outgoing => info.connect_outgoing(Some(seen_address)),
} }
} }
@ -1274,6 +1251,7 @@ impl BannedPeersCount {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use libp2p::core::multiaddr::Protocol;
use libp2p::core::Multiaddr; use libp2p::core::Multiaddr;
use slog::{o, Drain}; use slog::{o, Drain};
use std::net::{Ipv4Addr, Ipv6Addr}; use std::net::{Ipv4Addr, Ipv6Addr};

View File

@ -2,15 +2,15 @@ use super::client::Client;
use super::score::{PeerAction, Score, ScoreState}; use super::score::{PeerAction, Score, ScoreState};
use super::sync_status::SyncStatus; use super::sync_status::SyncStatus;
use crate::discovery::Eth2Enr; use crate::discovery::Eth2Enr;
use crate::Multiaddr;
use crate::{rpc::MetaData, types::Subnet}; use crate::{rpc::MetaData, types::Subnet};
use discv5::Enr; use discv5::Enr;
use libp2p::core::multiaddr::{Multiaddr, Protocol};
use serde::{ use serde::{
ser::{SerializeStruct, Serializer}, ser::{SerializeStruct, Serializer},
Serialize, Serialize,
}; };
use std::collections::HashSet; use std::collections::HashSet;
use std::net::{IpAddr, SocketAddr}; use std::net::IpAddr;
use std::time::Instant; use std::time::Instant;
use strum::AsRefStr; use strum::AsRefStr;
use types::EthSpec; use types::EthSpec;
@ -29,9 +29,9 @@ pub struct PeerInfo<T: EthSpec> {
/// The known listening addresses of this peer. This is given by identify and can be arbitrary /// The known listening addresses of this peer. This is given by identify and can be arbitrary
/// (including local IPs). /// (including local IPs).
listening_addresses: Vec<Multiaddr>, listening_addresses: Vec<Multiaddr>,
/// This is addresses we have physically seen and this is what we use for banning/un-banning /// These are the multiaddrs we have physically seen and is what we use for banning/un-banning
/// peers. /// peers.
seen_addresses: HashSet<SocketAddr>, seen_multiaddrs: HashSet<Multiaddr>,
/// The current syncing state of the peer. The state may be determined after it's initial /// The current syncing state of the peer. The state may be determined after it's initial
/// connection. /// connection.
sync_status: SyncStatus, sync_status: SyncStatus,
@ -60,7 +60,7 @@ impl<TSpec: EthSpec> Default for PeerInfo<TSpec> {
client: Client::default(), client: Client::default(),
connection_status: Default::default(), connection_status: Default::default(),
listening_addresses: Vec::new(), listening_addresses: Vec::new(),
seen_addresses: HashSet::new(), seen_multiaddrs: HashSet::new(),
subnets: HashSet::new(), subnets: HashSet::new(),
sync_status: SyncStatus::Unknown, sync_status: SyncStatus::Unknown,
meta_data: None, meta_data: None,
@ -227,15 +227,21 @@ impl<T: EthSpec> PeerInfo<T> {
} }
/// Returns the seen addresses of the peer. /// Returns the seen addresses of the peer.
pub fn seen_addresses(&self) -> impl Iterator<Item = &SocketAddr> + '_ { pub fn seen_multiaddrs(&self) -> impl Iterator<Item = &Multiaddr> + '_ {
self.seen_addresses.iter() self.seen_multiaddrs.iter()
} }
/// Returns a list of seen IP addresses for the peer. /// Returns a list of seen IP addresses for the peer.
pub fn seen_ip_addresses(&self) -> impl Iterator<Item = IpAddr> + '_ { pub fn seen_ip_addresses(&self) -> impl Iterator<Item = IpAddr> + '_ {
self.seen_addresses self.seen_multiaddrs.iter().filter_map(|multiaddr| {
.iter() multiaddr.iter().find_map(|protocol| {
.map(|socket_addr| socket_addr.ip()) match protocol {
Protocol::Ip4(ip) => Some(ip.into()),
Protocol::Ip6(ip) => Some(ip.into()),
_ => None, // Only care for IP addresses
}
})
})
} }
/// Returns the connection status of the peer. /// Returns the connection status of the peer.
@ -415,7 +421,7 @@ impl<T: EthSpec> PeerInfo<T> {
/// Modifies the status to Connected and increases the number of ingoing /// Modifies the status to Connected and increases the number of ingoing
/// connections by one /// connections by one
pub(super) fn connect_ingoing(&mut self, seen_address: Option<SocketAddr>) { pub(super) fn connect_ingoing(&mut self, seen_multiaddr: Option<Multiaddr>) {
match &mut self.connection_status { match &mut self.connection_status {
Connected { n_in, .. } => *n_in += 1, Connected { n_in, .. } => *n_in += 1,
Disconnected { .. } Disconnected { .. }
@ -428,14 +434,14 @@ impl<T: EthSpec> PeerInfo<T> {
} }
} }
if let Some(socket_addr) = seen_address { if let Some(multiaddr) = seen_multiaddr {
self.seen_addresses.insert(socket_addr); self.seen_multiaddrs.insert(multiaddr);
} }
} }
/// Modifies the status to Connected and increases the number of outgoing /// Modifies the status to Connected and increases the number of outgoing
/// connections by one /// connections by one
pub(super) fn connect_outgoing(&mut self, seen_address: Option<SocketAddr>) { pub(super) fn connect_outgoing(&mut self, seen_multiaddr: Option<Multiaddr>) {
match &mut self.connection_status { match &mut self.connection_status {
Connected { n_out, .. } => *n_out += 1, Connected { n_out, .. } => *n_out += 1,
Disconnected { .. } Disconnected { .. }
@ -447,8 +453,8 @@ impl<T: EthSpec> PeerInfo<T> {
self.connection_direction = Some(ConnectionDirection::Outgoing); self.connection_direction = Some(ConnectionDirection::Outgoing);
} }
} }
if let Some(ip_addr) = seen_address { if let Some(multiaddr) = seen_multiaddr {
self.seen_addresses.insert(ip_addr); self.seen_multiaddrs.insert(multiaddr);
} }
} }

View File

@ -160,8 +160,6 @@ impl<AppReqId: ReqId, TSpec: EthSpec> Network<AppReqId, TSpec> {
let meta_data = utils::load_or_build_metadata(&config.network_dir, &log); let meta_data = utils::load_or_build_metadata(&config.network_dir, &log);
let globals = NetworkGlobals::new( let globals = NetworkGlobals::new(
enr, enr,
config.listen_addrs().v4().map(|v4_addr| v4_addr.tcp_port),
config.listen_addrs().v6().map(|v6_addr| v6_addr.tcp_port),
meta_data, meta_data,
config config
.trusted_peers .trusted_peers
@ -371,8 +369,9 @@ impl<AppReqId: ReqId, TSpec: EthSpec> Network<AppReqId, TSpec> {
let (swarm, bandwidth) = { let (swarm, bandwidth) = {
// Set up the transport - tcp/ws with noise and mplex // Set up the transport - tcp/ws with noise and mplex
let (transport, bandwidth) = build_transport(local_keypair.clone()) let (transport, bandwidth) =
.map_err(|e| format!("Failed to build transport: {:?}", e))?; build_transport(local_keypair.clone(), !config.disable_quic_support)
.map_err(|e| format!("Failed to build transport: {:?}", e))?;
// use the executor for libp2p // use the executor for libp2p
struct Executor(task_executor::TaskExecutor); struct Executor(task_executor::TaskExecutor);
@ -427,9 +426,16 @@ impl<AppReqId: ReqId, TSpec: EthSpec> Network<AppReqId, TSpec> {
async fn start(&mut self, config: &crate::NetworkConfig) -> error::Result<()> { async fn start(&mut self, config: &crate::NetworkConfig) -> error::Result<()> {
let enr = self.network_globals.local_enr(); let enr = self.network_globals.local_enr();
info!(self.log, "Libp2p Starting"; "peer_id" => %enr.peer_id(), "bandwidth_config" => format!("{}-{}", config.network_load, NetworkLoad::from(config.network_load).name)); info!(self.log, "Libp2p Starting"; "peer_id" => %enr.peer_id(), "bandwidth_config" => format!("{}-{}", config.network_load, NetworkLoad::from(config.network_load).name));
debug!(self.log, "Attempting to open listening ports"; config.listen_addrs(), "discovery_enabled" => !config.disable_discovery); debug!(self.log, "Attempting to open listening ports"; config.listen_addrs(), "discovery_enabled" => !config.disable_discovery, "quic_enabled" => !config.disable_quic_support);
for listen_multiaddr in config.listen_addrs().libp2p_addresses() {
// If QUIC is disabled, ignore listening on QUIC ports
if config.disable_quic_support
&& listen_multiaddr.iter().any(|v| v == MProtocol::QuicV1)
{
continue;
}
for listen_multiaddr in config.listen_addrs().tcp_addresses() {
match self.swarm.listen_on(listen_multiaddr.clone()) { match self.swarm.listen_on(listen_multiaddr.clone()) {
Ok(_) => { Ok(_) => {
let mut log_address = listen_multiaddr; let mut log_address = listen_multiaddr;
@ -470,6 +476,20 @@ impl<AppReqId: ReqId, TSpec: EthSpec> Network<AppReqId, TSpec> {
boot_nodes.dedup(); boot_nodes.dedup();
for bootnode_enr in boot_nodes { for bootnode_enr in boot_nodes {
// If QUIC is enabled, attempt QUIC connections first
if !config.disable_quic_support {
for quic_multiaddr in &bootnode_enr.multiaddr_quic() {
if !self
.network_globals
.peers
.read()
.is_connected_or_dialing(&bootnode_enr.peer_id())
{
dial(quic_multiaddr.clone());
}
}
}
for multiaddr in &bootnode_enr.multiaddr() { for multiaddr in &bootnode_enr.multiaddr() {
// ignore udp multiaddr if it exists // ignore udp multiaddr if it exists
let components = multiaddr.iter().collect::<Vec<_>>(); let components = multiaddr.iter().collect::<Vec<_>>();
@ -1058,30 +1078,27 @@ impl<AppReqId: ReqId, TSpec: EthSpec> Network<AppReqId, TSpec> {
} }
} }
/// Dial cached enrs in discovery service that are in the given `subnet_id` and aren't /// Dial cached Enrs in discovery service that are in the given `subnet_id` and aren't
/// in Connected, Dialing or Banned state. /// in Connected, Dialing or Banned state.
fn dial_cached_enrs_in_subnet(&mut self, subnet: Subnet) { fn dial_cached_enrs_in_subnet(&mut self, subnet: Subnet) {
let predicate = subnet_predicate::<TSpec>(vec![subnet], &self.log); let predicate = subnet_predicate::<TSpec>(vec![subnet], &self.log);
let peers_to_dial: Vec<PeerId> = self let peers_to_dial: Vec<Enr> = self
.discovery() .discovery()
.cached_enrs() .cached_enrs()
.filter_map(|(peer_id, enr)| { .filter_map(|(_peer_id, enr)| {
let peers = self.network_globals.peers.read(); if predicate(enr) {
if predicate(enr) && peers.should_dial(peer_id) { Some(enr.clone())
Some(*peer_id)
} else { } else {
None None
} }
}) })
.collect(); .collect();
for peer_id in peers_to_dial {
debug!(self.log, "Dialing cached ENR peer"; "peer_id" => %peer_id);
// Remove the ENR from the cache to prevent continual re-dialing on disconnects
self.discovery_mut().remove_cached_enr(&peer_id); // Remove the ENR from the cache to prevent continual re-dialing on disconnects
// For any dial event, inform the peer manager for enr in peers_to_dial {
let enr = self.discovery_mut().enr_of_peer(&peer_id); debug!(self.log, "Dialing cached ENR peer"; "peer_id" => %enr.peer_id());
self.peer_manager_mut().dial_peer(&peer_id, enr); self.discovery_mut().remove_cached_enr(&enr.peer_id());
self.peer_manager_mut().dial_peer(enr);
} }
} }
@ -1388,22 +1405,6 @@ impl<AppReqId: ReqId, TSpec: EthSpec> Network<AppReqId, TSpec> {
} }
} }
/// Handle a discovery event.
fn inject_discovery_event(
&mut self,
event: DiscoveredPeers,
) -> Option<NetworkEvent<AppReqId, TSpec>> {
let DiscoveredPeers { peers } = event;
let to_dial_peers = self.peer_manager_mut().peers_discovered(peers);
for peer_id in to_dial_peers {
debug!(self.log, "Dialing discovered peer"; "peer_id" => %peer_id);
// For any dial event, inform the peer manager
let enr = self.discovery_mut().enr_of_peer(&peer_id);
self.peer_manager_mut().dial_peer(&peer_id, enr);
}
None
}
/// Handle an identify event. /// Handle an identify event.
fn inject_identify_event( fn inject_identify_event(
&mut self, &mut self,
@ -1504,7 +1505,14 @@ impl<AppReqId: ReqId, TSpec: EthSpec> Network<AppReqId, TSpec> {
BehaviourEvent::BannedPeers(void) => void::unreachable(void), 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),
BehaviourEvent::Discovery(de) => self.inject_discovery_event(de), // Inform the peer manager about discovered peers.
//
// The peer manager will subsequently decide which peers need to be dialed and then dial
// them.
BehaviourEvent::Discovery(DiscoveredPeers { peers }) => {
self.peer_manager_mut().peers_discovered(peers);
None
}
BehaviourEvent::Identify(ie) => self.inject_identify_event(ie), BehaviourEvent::Identify(ie) => self.inject_identify_event(ie),
BehaviourEvent::PeerManager(pe) => self.inject_pm_event(pe), BehaviourEvent::PeerManager(pe) => self.inject_pm_event(pe),
BehaviourEvent::ConnectionLimits(le) => void::unreachable(le), BehaviourEvent::ConnectionLimits(le) => void::unreachable(le),
@ -1536,7 +1544,7 @@ impl<AppReqId: ReqId, TSpec: EthSpec> Network<AppReqId, TSpec> {
format!("Dialing local peer id {endpoint:?}") format!("Dialing local peer id {endpoint:?}")
} }
libp2p::swarm::ListenError::Denied { cause } => { libp2p::swarm::ListenError::Denied { cause } => {
format!("Connection was denied with cause {cause}") format!("Connection was denied with cause: {cause:?}")
} }
libp2p::swarm::ListenError::Transport(t) => match t { libp2p::swarm::ListenError::Transport(t) => match t {
libp2p::TransportError::MultiaddrNotSupported(m) => { libp2p::TransportError::MultiaddrNotSupported(m) => {
@ -1586,13 +1594,7 @@ impl<AppReqId: ReqId, TSpec: EthSpec> Network<AppReqId, TSpec> {
None None
} }
} }
SwarmEvent::Dialing { SwarmEvent::Dialing { .. } => None,
peer_id,
connection_id: _,
} => {
debug!(self.log, "Swarm Dialing"; "peer_id" => ?peer_id);
None
}
}; };
if let Some(ev) = maybe_event { if let Some(ev) = maybe_event {

View File

@ -4,11 +4,13 @@ use crate::types::{
error, EnrAttestationBitfield, EnrSyncCommitteeBitfield, GossipEncoding, GossipKind, error, EnrAttestationBitfield, EnrSyncCommitteeBitfield, GossipEncoding, GossipKind,
}; };
use crate::{GossipTopic, NetworkConfig}; use crate::{GossipTopic, NetworkConfig};
use futures::future::Either;
use libp2p::bandwidth::BandwidthSinks; use libp2p::bandwidth::BandwidthSinks;
use libp2p::core::{multiaddr::Multiaddr, muxing::StreamMuxerBox, transport::Boxed}; use libp2p::core::{multiaddr::Multiaddr, muxing::StreamMuxerBox, transport::Boxed};
use libp2p::gossipsub; use libp2p::gossipsub;
use libp2p::identity::{secp256k1, Keypair}; use libp2p::identity::{secp256k1, Keypair};
use libp2p::{core, noise, yamux, PeerId, Transport, TransportExt}; use libp2p::{core, noise, yamux, PeerId, Transport, TransportExt};
use libp2p_quic;
use prometheus_client::registry::Registry; use prometheus_client::registry::Registry;
use slog::{debug, warn}; use slog::{debug, warn};
use ssz::Decode; use ssz::Decode;
@ -37,19 +39,12 @@ pub struct Context<'a> {
type BoxedTransport = Boxed<(PeerId, StreamMuxerBox)>; type BoxedTransport = Boxed<(PeerId, StreamMuxerBox)>;
/// The implementation supports TCP/IP, WebSockets over TCP/IP, noise as the encryption layer, and /// The implementation supports TCP/IP, QUIC (experimental) over UDP, noise as the encryption layer, and
/// mplex as the multiplexing layer. /// mplex/yamux as the multiplexing layer (when using TCP).
pub fn build_transport( pub fn build_transport(
local_private_key: Keypair, local_private_key: Keypair,
quic_support: bool,
) -> std::io::Result<(BoxedTransport, Arc<BandwidthSinks>)> { ) -> std::io::Result<(BoxedTransport, Arc<BandwidthSinks>)> {
let tcp = libp2p::tcp::tokio::Transport::new(libp2p::tcp::Config::default().nodelay(true));
let transport = libp2p::dns::TokioDnsConfig::system(tcp)?;
#[cfg(feature = "libp2p-websocket")]
let transport = {
let trans_clone = transport.clone();
transport.or_transport(libp2p::websocket::WsConfig::new(trans_clone))
};
// mplex config // mplex config
let mut mplex_config = libp2p_mplex::MplexConfig::new(); let mut mplex_config = libp2p_mplex::MplexConfig::new();
mplex_config.set_max_buffer_size(256); mplex_config.set_max_buffer_size(256);
@ -58,18 +53,34 @@ pub fn build_transport(
// yamux config // yamux config
let mut yamux_config = yamux::Config::default(); let mut yamux_config = yamux::Config::default();
yamux_config.set_window_update_mode(yamux::WindowUpdateMode::on_read()); yamux_config.set_window_update_mode(yamux::WindowUpdateMode::on_read());
let (transport, bandwidth) = transport
// Creates the TCP transport layer
let tcp = libp2p::tcp::tokio::Transport::new(libp2p::tcp::Config::default().nodelay(true))
.upgrade(core::upgrade::Version::V1) .upgrade(core::upgrade::Version::V1)
.authenticate(generate_noise_config(&local_private_key)) .authenticate(generate_noise_config(&local_private_key))
.multiplex(core::upgrade::SelectUpgrade::new( .multiplex(core::upgrade::SelectUpgrade::new(
yamux_config, yamux_config,
mplex_config, mplex_config,
)) ))
.timeout(Duration::from_secs(10)) .timeout(Duration::from_secs(10));
.boxed()
.with_bandwidth_logging(); let (transport, bandwidth) = if quic_support {
// Enables Quic
// The default quic configuration suits us for now.
let quic_config = libp2p_quic::Config::new(&local_private_key);
tcp.or_transport(libp2p_quic::tokio::Transport::new(quic_config))
.map(|either_output, _| match either_output {
Either::Left((peer_id, muxer)) => (peer_id, StreamMuxerBox::new(muxer)),
Either::Right((peer_id, muxer)) => (peer_id, StreamMuxerBox::new(muxer)),
})
.with_bandwidth_logging()
} else {
tcp.with_bandwidth_logging()
};
// // Enables DNS over the transport.
let transport = libp2p::dns::TokioDnsConfig::system(transport)?.boxed();
// Authentication
Ok((transport, bandwidth)) Ok((transport, bandwidth))
} }

View File

@ -16,10 +16,6 @@ pub struct NetworkGlobals<TSpec: EthSpec> {
pub peer_id: RwLock<PeerId>, pub peer_id: RwLock<PeerId>,
/// Listening multiaddrs. /// Listening multiaddrs.
pub listen_multiaddrs: RwLock<Vec<Multiaddr>>, pub listen_multiaddrs: RwLock<Vec<Multiaddr>>,
/// The TCP port that the libp2p service is listening on over Ipv4.
listen_port_tcp4: Option<u16>,
/// The TCP port that the libp2p service is listening on over Ipv6.
listen_port_tcp6: Option<u16>,
/// The collection of known peers. /// The collection of known peers.
pub peers: RwLock<PeerDB<TSpec>>, pub peers: RwLock<PeerDB<TSpec>>,
// The local meta data of our node. // The local meta data of our node.
@ -35,8 +31,6 @@ pub struct NetworkGlobals<TSpec: EthSpec> {
impl<TSpec: EthSpec> NetworkGlobals<TSpec> { impl<TSpec: EthSpec> NetworkGlobals<TSpec> {
pub fn new( pub fn new(
enr: Enr, enr: Enr,
listen_port_tcp4: Option<u16>,
listen_port_tcp6: Option<u16>,
local_metadata: MetaData<TSpec>, local_metadata: MetaData<TSpec>,
trusted_peers: Vec<PeerId>, trusted_peers: Vec<PeerId>,
disable_peer_scoring: bool, disable_peer_scoring: bool,
@ -46,8 +40,6 @@ impl<TSpec: EthSpec> NetworkGlobals<TSpec> {
local_enr: RwLock::new(enr.clone()), local_enr: RwLock::new(enr.clone()),
peer_id: RwLock::new(enr.peer_id()), peer_id: RwLock::new(enr.peer_id()),
listen_multiaddrs: RwLock::new(Vec::new()), listen_multiaddrs: RwLock::new(Vec::new()),
listen_port_tcp4,
listen_port_tcp6,
local_metadata: RwLock::new(local_metadata), local_metadata: RwLock::new(local_metadata),
peers: RwLock::new(PeerDB::new(trusted_peers, disable_peer_scoring, log)), peers: RwLock::new(PeerDB::new(trusted_peers, disable_peer_scoring, log)),
gossipsub_subscriptions: RwLock::new(HashSet::new()), gossipsub_subscriptions: RwLock::new(HashSet::new()),
@ -72,16 +64,6 @@ impl<TSpec: EthSpec> NetworkGlobals<TSpec> {
self.listen_multiaddrs.read().clone() self.listen_multiaddrs.read().clone()
} }
/// Returns the libp2p TCP port that this node has been configured to listen on.
pub fn listen_port_tcp4(&self) -> Option<u16> {
self.listen_port_tcp4
}
/// Returns the UDP discovery port that this node has been configured to listen on.
pub fn listen_port_tcp6(&self) -> Option<u16> {
self.listen_port_tcp6
}
/// Returns the number of libp2p connected peers. /// Returns the number of libp2p connected peers.
pub fn connected_peers(&self) -> usize { pub fn connected_peers(&self) -> usize {
self.peers.read().connected_peer_ids().count() self.peers.read().connected_peer_ids().count()
@ -139,8 +121,6 @@ impl<TSpec: EthSpec> NetworkGlobals<TSpec> {
let enr = discv5::enr::EnrBuilder::new("v4").build(&enr_key).unwrap(); let enr = discv5::enr::EnrBuilder::new("v4").build(&enr_key).unwrap();
NetworkGlobals::new( NetworkGlobals::new(
enr, enr,
Some(9000),
None,
MetaData::V2(MetaDataV2 { MetaData::V2(MetaDataV2 {
seq_number: 0, seq_number: 0,
attnets: Default::default(), attnets: Default::default(),

View File

@ -13,7 +13,6 @@ use tokio::runtime::Runtime;
use types::{ use types::{
ChainSpec, EnrForkId, Epoch, EthSpec, ForkContext, ForkName, Hash256, MinimalEthSpec, Slot, ChainSpec, EnrForkId, Epoch, EthSpec, ForkContext, ForkName, Hash256, MinimalEthSpec, Slot,
}; };
use unused_port::unused_tcp4_port;
type E = MinimalEthSpec; type E = MinimalEthSpec;
type ReqId = usize; type ReqId = usize;
@ -71,15 +70,19 @@ pub fn build_log(level: slog::Level, enabled: bool) -> slog::Logger {
} }
} }
pub fn build_config(port: u16, mut boot_nodes: Vec<Enr>) -> NetworkConfig { pub fn build_config(mut boot_nodes: Vec<Enr>) -> NetworkConfig {
let mut config = NetworkConfig::default(); let mut config = NetworkConfig::default();
// Find unused ports by using the 0 port.
let port = 0;
let random_path: u16 = rand::random();
let path = TempBuilder::new() let path = TempBuilder::new()
.prefix(&format!("libp2p_test{}", port)) .prefix(&format!("libp2p_test_{}", random_path))
.tempdir() .tempdir()
.unwrap(); .unwrap();
config.set_ipv4_listening_address(std::net::Ipv4Addr::UNSPECIFIED, port, port); config.set_ipv4_listening_address(std::net::Ipv4Addr::UNSPECIFIED, port, port, port);
config.enr_udp4_port = Some(port);
config.enr_address = (Some(std::net::Ipv4Addr::LOCALHOST), None); config.enr_address = (Some(std::net::Ipv4Addr::LOCALHOST), None);
config.boot_nodes_enr.append(&mut boot_nodes); config.boot_nodes_enr.append(&mut boot_nodes);
config.network_dir = path.into_path(); config.network_dir = path.into_path();
@ -99,8 +102,7 @@ pub async fn build_libp2p_instance(
fork_name: ForkName, fork_name: ForkName,
spec: &ChainSpec, spec: &ChainSpec,
) -> Libp2pInstance { ) -> Libp2pInstance {
let port = unused_tcp4_port().unwrap(); let config = build_config(boot_nodes);
let config = build_config(port, boot_nodes);
// launch libp2p service // launch libp2p service
let (signal, exit) = exit_future::signal(); let (signal, exit) = exit_future::signal();
@ -127,6 +129,12 @@ pub fn get_enr(node: &LibP2PService<ReqId, E>) -> Enr {
node.local_enr() node.local_enr()
} }
// Protocol for the node pair connection.
pub enum Protocol {
Tcp,
Quic,
}
// Constructs a pair of nodes with separate loggers. The sender dials the receiver. // Constructs a pair of nodes with separate loggers. The sender dials the receiver.
// This returns a (sender, receiver) pair. // This returns a (sender, receiver) pair.
#[allow(dead_code)] #[allow(dead_code)]
@ -135,6 +143,7 @@ pub async fn build_node_pair(
log: &slog::Logger, log: &slog::Logger,
fork_name: ForkName, fork_name: ForkName,
spec: &ChainSpec, spec: &ChainSpec,
protocol: Protocol,
) -> (Libp2pInstance, Libp2pInstance) { ) -> (Libp2pInstance, Libp2pInstance) {
let sender_log = log.new(o!("who" => "sender")); let sender_log = log.new(o!("who" => "sender"));
let receiver_log = log.new(o!("who" => "receiver")); let receiver_log = log.new(o!("who" => "receiver"));
@ -142,33 +151,57 @@ pub async fn build_node_pair(
let mut sender = build_libp2p_instance(rt.clone(), vec![], sender_log, fork_name, spec).await; let mut sender = build_libp2p_instance(rt.clone(), vec![], sender_log, fork_name, spec).await;
let mut receiver = build_libp2p_instance(rt, vec![], receiver_log, fork_name, spec).await; let mut receiver = build_libp2p_instance(rt, vec![], receiver_log, fork_name, spec).await;
let receiver_multiaddr = receiver.local_enr().multiaddr()[1].clone();
// let the two nodes set up listeners // let the two nodes set up listeners
let sender_fut = async { let sender_fut = async {
loop { loop {
if let NetworkEvent::NewListenAddr(_) = sender.next_event().await { if let NetworkEvent::NewListenAddr(addr) = sender.next_event().await {
return; // Only end once we've listened on the protocol we care about
match protocol {
Protocol::Tcp => {
if addr.iter().any(|multiaddr_proto| {
matches!(multiaddr_proto, libp2p::multiaddr::Protocol::Tcp(_))
}) {
return addr;
}
}
Protocol::Quic => {
if addr.iter().any(|multiaddr_proto| {
matches!(multiaddr_proto, libp2p::multiaddr::Protocol::QuicV1)
}) {
return addr;
}
}
}
} }
} }
}; };
let receiver_fut = async { let receiver_fut = async {
loop { loop {
if let NetworkEvent::NewListenAddr(_) = receiver.next_event().await { if let NetworkEvent::NewListenAddr(addr) = receiver.next_event().await {
return; match protocol {
Protocol::Tcp => {
if addr.iter().any(|multiaddr_proto| {
matches!(multiaddr_proto, libp2p::multiaddr::Protocol::Tcp(_))
}) {
return addr;
}
}
Protocol::Quic => {
if addr.iter().any(|multiaddr_proto| {
matches!(multiaddr_proto, libp2p::multiaddr::Protocol::QuicV1)
}) {
return addr;
}
}
}
} }
} }
}; };
let joined = futures::future::join(sender_fut, receiver_fut); let joined = futures::future::join(sender_fut, receiver_fut);
// wait for either both nodes to listen or a timeout let receiver_multiaddr = joined.await.1;
tokio::select! {
_ = tokio::time::sleep(Duration::from_millis(500)) => {}
_ = joined => {}
}
// sender.dial_peer(peer_id);
match sender.testing_dial(receiver_multiaddr.clone()) { match sender.testing_dial(receiver_multiaddr.clone()) {
Ok(()) => { Ok(()) => {
debug!(log, "Sender dialed receiver"; "address" => format!("{:?}", receiver_multiaddr)) debug!(log, "Sender dialed receiver"; "address" => format!("{:?}", receiver_multiaddr))

View File

@ -1,4 +1,8 @@
#![cfg(test)] #![cfg(test)]
mod common;
use common::Protocol;
use lighthouse_network::rpc::methods::*; use lighthouse_network::rpc::methods::*;
use lighthouse_network::{rpc::max_rpc_size, NetworkEvent, ReportSource, Request, Response}; use lighthouse_network::{rpc::max_rpc_size, NetworkEvent, ReportSource, Request, Response};
use slog::{debug, warn, Level}; use slog::{debug, warn, Level};
@ -14,8 +18,6 @@ use types::{
SignedBeaconBlock, Slot, SignedBeaconBlock, Slot,
}; };
mod common;
type E = MinimalEthSpec; type E = MinimalEthSpec;
/// Merge block with length < max_rpc_size. /// Merge block with length < max_rpc_size.
@ -49,7 +51,7 @@ fn merge_block_large(fork_context: &ForkContext, spec: &ChainSpec) -> BeaconBloc
// Tests the STATUS RPC message // Tests the STATUS RPC message
#[test] #[test]
#[allow(clippy::single_match)] #[allow(clippy::single_match)]
fn test_status_rpc() { fn test_tcp_status_rpc() {
// set up the logging. The level and enabled logging or not // set up the logging. The level and enabled logging or not
let log_level = Level::Debug; let log_level = Level::Debug;
let enable_logging = false; let enable_logging = false;
@ -62,8 +64,14 @@ fn test_status_rpc() {
rt.block_on(async { rt.block_on(async {
// get sender/receiver // get sender/receiver
let (mut sender, mut receiver) = let (mut sender, mut receiver) = common::build_node_pair(
common::build_node_pair(Arc::downgrade(&rt), &log, ForkName::Base, &spec).await; Arc::downgrade(&rt),
&log,
ForkName::Base,
&spec,
Protocol::Tcp,
)
.await;
// Dummy STATUS RPC message // Dummy STATUS RPC message
let rpc_request = Request::Status(StatusMessage { let rpc_request = Request::Status(StatusMessage {
@ -141,7 +149,7 @@ fn test_status_rpc() {
// Tests a streamed BlocksByRange RPC Message // Tests a streamed BlocksByRange RPC Message
#[test] #[test]
#[allow(clippy::single_match)] #[allow(clippy::single_match)]
fn test_blocks_by_range_chunked_rpc() { fn test_tcp_blocks_by_range_chunked_rpc() {
// set up the logging. The level and enabled logging or not // set up the logging. The level and enabled logging or not
let log_level = Level::Debug; let log_level = Level::Debug;
let enable_logging = false; let enable_logging = false;
@ -156,8 +164,14 @@ fn test_blocks_by_range_chunked_rpc() {
rt.block_on(async { rt.block_on(async {
// get sender/receiver // get sender/receiver
let (mut sender, mut receiver) = let (mut sender, mut receiver) = common::build_node_pair(
common::build_node_pair(Arc::downgrade(&rt), &log, ForkName::Merge, &spec).await; Arc::downgrade(&rt),
&log,
ForkName::Merge,
&spec,
Protocol::Tcp,
)
.await;
// BlocksByRange Request // BlocksByRange Request
let rpc_request = Request::BlocksByRange(BlocksByRangeRequest::new(0, messages_to_send)); let rpc_request = Request::BlocksByRange(BlocksByRangeRequest::new(0, messages_to_send));
@ -282,8 +296,14 @@ fn test_blobs_by_range_chunked_rpc() {
rt.block_on(async { rt.block_on(async {
// get sender/receiver // get sender/receiver
let spec = E::default_spec(); let spec = E::default_spec();
let (mut sender, mut receiver) = let (mut sender, mut receiver) = common::build_node_pair(
common::build_node_pair(Arc::downgrade(&rt), &log, ForkName::Deneb, &spec).await; Arc::downgrade(&rt),
&log,
ForkName::Deneb,
&spec,
Protocol::Tcp,
)
.await;
// BlobsByRange Request // BlobsByRange Request
let rpc_request = Request::BlobsByRange(BlobsByRangeRequest { let rpc_request = Request::BlobsByRange(BlobsByRangeRequest {
@ -373,7 +393,7 @@ fn test_blobs_by_range_chunked_rpc() {
// Tests rejection of blocks over `MAX_RPC_SIZE`. // Tests rejection of blocks over `MAX_RPC_SIZE`.
#[test] #[test]
#[allow(clippy::single_match)] #[allow(clippy::single_match)]
fn test_blocks_by_range_over_limit() { fn test_tcp_blocks_by_range_over_limit() {
// set up the logging. The level and enabled logging or not // set up the logging. The level and enabled logging or not
let log_level = Level::Debug; let log_level = Level::Debug;
let enable_logging = false; let enable_logging = false;
@ -388,8 +408,14 @@ fn test_blocks_by_range_over_limit() {
rt.block_on(async { rt.block_on(async {
// get sender/receiver // get sender/receiver
let (mut sender, mut receiver) = let (mut sender, mut receiver) = common::build_node_pair(
common::build_node_pair(Arc::downgrade(&rt), &log, ForkName::Merge, &spec).await; Arc::downgrade(&rt),
&log,
ForkName::Merge,
&spec,
Protocol::Tcp,
)
.await;
// BlocksByRange Request // BlocksByRange Request
let rpc_request = Request::BlocksByRange(BlocksByRangeRequest::new(0, messages_to_send)); let rpc_request = Request::BlocksByRange(BlocksByRangeRequest::new(0, messages_to_send));
@ -456,7 +482,7 @@ fn test_blocks_by_range_over_limit() {
// Tests that a streamed BlocksByRange RPC Message terminates when all expected chunks were received // Tests that a streamed BlocksByRange RPC Message terminates when all expected chunks were received
#[test] #[test]
fn test_blocks_by_range_chunked_rpc_terminates_correctly() { fn test_tcp_blocks_by_range_chunked_rpc_terminates_correctly() {
// set up the logging. The level and enabled logging or not // set up the logging. The level and enabled logging or not
let log_level = Level::Debug; let log_level = Level::Debug;
let enable_logging = false; let enable_logging = false;
@ -472,8 +498,14 @@ fn test_blocks_by_range_chunked_rpc_terminates_correctly() {
rt.block_on(async { rt.block_on(async {
// get sender/receiver // get sender/receiver
let (mut sender, mut receiver) = let (mut sender, mut receiver) = common::build_node_pair(
common::build_node_pair(Arc::downgrade(&rt), &log, ForkName::Base, &spec).await; Arc::downgrade(&rt),
&log,
ForkName::Base,
&spec,
Protocol::Tcp,
)
.await;
// BlocksByRange Request // BlocksByRange Request
let rpc_request = Request::BlocksByRange(BlocksByRangeRequest::new(0, messages_to_send)); let rpc_request = Request::BlocksByRange(BlocksByRangeRequest::new(0, messages_to_send));
@ -582,7 +614,7 @@ fn test_blocks_by_range_chunked_rpc_terminates_correctly() {
// Tests an empty response to a BlocksByRange RPC Message // Tests an empty response to a BlocksByRange RPC Message
#[test] #[test]
#[allow(clippy::single_match)] #[allow(clippy::single_match)]
fn test_blocks_by_range_single_empty_rpc() { fn test_tcp_blocks_by_range_single_empty_rpc() {
// set up the logging. The level and enabled logging or not // set up the logging. The level and enabled logging or not
let log_level = Level::Trace; let log_level = Level::Trace;
let enable_logging = false; let enable_logging = false;
@ -594,8 +626,14 @@ fn test_blocks_by_range_single_empty_rpc() {
rt.block_on(async { rt.block_on(async {
// get sender/receiver // get sender/receiver
let (mut sender, mut receiver) = let (mut sender, mut receiver) = common::build_node_pair(
common::build_node_pair(Arc::downgrade(&rt), &log, ForkName::Base, &spec).await; Arc::downgrade(&rt),
&log,
ForkName::Base,
&spec,
Protocol::Tcp,
)
.await;
// BlocksByRange Request // BlocksByRange Request
let rpc_request = Request::BlocksByRange(BlocksByRangeRequest::new(0, 10)); let rpc_request = Request::BlocksByRange(BlocksByRangeRequest::new(0, 10));
@ -682,7 +720,7 @@ fn test_blocks_by_range_single_empty_rpc() {
// serves to test the snappy framing format as well. // serves to test the snappy framing format as well.
#[test] #[test]
#[allow(clippy::single_match)] #[allow(clippy::single_match)]
fn test_blocks_by_root_chunked_rpc() { fn test_tcp_blocks_by_root_chunked_rpc() {
// set up the logging. The level and enabled logging or not // set up the logging. The level and enabled logging or not
let log_level = Level::Debug; let log_level = Level::Debug;
let enable_logging = false; let enable_logging = false;
@ -695,8 +733,14 @@ fn test_blocks_by_root_chunked_rpc() {
let rt = Arc::new(Runtime::new().unwrap()); let rt = Arc::new(Runtime::new().unwrap());
// get sender/receiver // get sender/receiver
rt.block_on(async { rt.block_on(async {
let (mut sender, mut receiver) = let (mut sender, mut receiver) = common::build_node_pair(
common::build_node_pair(Arc::downgrade(&rt), &log, ForkName::Merge, &spec).await; Arc::downgrade(&rt),
&log,
ForkName::Merge,
&spec,
Protocol::Tcp,
)
.await;
// BlocksByRoot Request // BlocksByRoot Request
let rpc_request = let rpc_request =
@ -808,7 +852,7 @@ fn test_blocks_by_root_chunked_rpc() {
// Tests a streamed, chunked BlocksByRoot RPC Message terminates when all expected reponses have been received // Tests a streamed, chunked BlocksByRoot RPC Message terminates when all expected reponses have been received
#[test] #[test]
fn test_blocks_by_root_chunked_rpc_terminates_correctly() { fn test_tcp_blocks_by_root_chunked_rpc_terminates_correctly() {
// set up the logging. The level and enabled logging or not // set up the logging. The level and enabled logging or not
let log_level = Level::Debug; let log_level = Level::Debug;
let enable_logging = false; let enable_logging = false;
@ -822,8 +866,14 @@ fn test_blocks_by_root_chunked_rpc_terminates_correctly() {
let rt = Arc::new(Runtime::new().unwrap()); let rt = Arc::new(Runtime::new().unwrap());
// get sender/receiver // get sender/receiver
rt.block_on(async { rt.block_on(async {
let (mut sender, mut receiver) = let (mut sender, mut receiver) = common::build_node_pair(
common::build_node_pair(Arc::downgrade(&rt), &log, ForkName::Base, &spec).await; Arc::downgrade(&rt),
&log,
ForkName::Base,
&spec,
Protocol::Tcp,
)
.await;
// BlocksByRoot Request // BlocksByRoot Request
let rpc_request = let rpc_request =
@ -939,14 +989,9 @@ fn test_blocks_by_root_chunked_rpc_terminates_correctly() {
}) })
} }
// Tests a Goodbye RPC message /// Establishes a pair of nodes and disconnects the pair based on the selected protocol via an RPC
#[test] /// Goodbye message.
#[allow(clippy::single_match)] fn goodbye_test(log_level: Level, enable_logging: bool, protocol: Protocol) {
fn test_goodbye_rpc() {
// set up the logging. The level and enabled logging or not
let log_level = Level::Trace;
let enable_logging = false;
let log = common::build_log(log_level, enable_logging); let log = common::build_log(log_level, enable_logging);
let rt = Arc::new(Runtime::new().unwrap()); let rt = Arc::new(Runtime::new().unwrap());
@ -956,7 +1001,8 @@ fn test_goodbye_rpc() {
// get sender/receiver // get sender/receiver
rt.block_on(async { rt.block_on(async {
let (mut sender, mut receiver) = let (mut sender, mut receiver) =
common::build_node_pair(Arc::downgrade(&rt), &log, ForkName::Base, &spec).await; common::build_node_pair(Arc::downgrade(&rt), &log, ForkName::Base, &spec, protocol)
.await;
// build the sender future // build the sender future
let sender_future = async { let sender_future = async {
@ -982,12 +1028,9 @@ fn test_goodbye_rpc() {
// build the receiver future // build the receiver future
let receiver_future = async { let receiver_future = async {
loop { loop {
match receiver.next_event().await { if let NetworkEvent::PeerDisconnected(_) = receiver.next_event().await {
NetworkEvent::PeerDisconnected(_) => { // Should receive sent RPC request
// Should receive sent RPC request return;
return;
}
_ => {} // Ignore other events
} }
} }
}; };
@ -1002,3 +1045,23 @@ fn test_goodbye_rpc() {
} }
}) })
} }
// Tests a Goodbye RPC message
#[test]
#[allow(clippy::single_match)]
fn tcp_test_goodbye_rpc() {
// set up the logging. The level and enabled logging or not
let log_level = Level::Debug;
let enable_logging = true;
goodbye_test(log_level, enable_logging, Protocol::Tcp);
}
// Tests a Goodbye RPC message
#[test]
#[allow(clippy::single_match)]
fn quic_test_goodbye_rpc() {
// set up the logging. The level and enabled logging or not
let log_level = Level::Debug;
let enable_logging = true;
goodbye_test(log_level, enable_logging, Protocol::Quic);
}

View File

@ -2,53 +2,54 @@
name = "network" name = "network"
version = "0.2.0" version = "0.2.0"
authors = ["Sigma Prime <contact@sigmaprime.io>"] authors = ["Sigma Prime <contact@sigmaprime.io>"]
edition = "2021" edition = { workspace = true }
[dev-dependencies] [dev-dependencies]
sloggers = { version = "2.1.1", features = ["json"] } sloggers = { workspace = true }
genesis = { path = "../genesis" } genesis = { workspace = true }
matches = "0.1.8" matches = "0.1.8"
exit-future = "0.2.0" exit-future = { workspace = true }
slog-term = "2.6.0" slog-term = { workspace = true }
slog-async = "2.5.0" slog-async = { workspace = true }
eth2 = {path="../../common/eth2"} eth2 = { workspace = true }
[dependencies] [dependencies]
beacon_chain = { path = "../beacon_chain" } beacon_chain = { workspace = true }
store = { path = "../store" } store = { workspace = true }
lighthouse_network = { path = "../lighthouse_network" } lighthouse_network = { workspace = true }
types = { path = "../../consensus/types" } types = { workspace = true }
slot_clock = { path = "../../common/slot_clock" } slot_clock = { workspace = true }
slog = { version = "2.5.2", features = ["max_level_trace", "nested-values"] } slog = { workspace = true }
hex = "0.4.2" hex = { workspace = true }
ethereum_ssz = "0.5.0" ethereum_ssz = { workspace = true }
ssz_types = "0.5.4" ssz_types = { workspace = true }
futures = "0.3.7" futures = { workspace = true }
error-chain = "0.12.4" error-chain = { workspace = true }
tokio = { version = "1.14.0", features = ["full"] } tokio = { workspace = true }
tokio-stream = "0.1.3" tokio-stream = { workspace = true }
smallvec = "1.6.1" smallvec = { workspace = true }
rand = "0.8.5" rand = { workspace = true }
fnv = "1.0.7" fnv = { workspace = true }
rlp = "0.5.0" rlp = "0.5.0"
lazy_static = "1.4.0" lazy_static = { workspace = true }
lighthouse_metrics = { path = "../../common/lighthouse_metrics" } lighthouse_metrics = { workspace = true }
logging = { path = "../../common/logging" } logging = { workspace = true }
task_executor = { path = "../../common/task_executor" } task_executor = { workspace = true }
igd = "0.12.1" igd = "0.12.1"
itertools = "0.10.0" itertools = { workspace = true }
lru_cache = { path = "../../common/lru_cache" } num_cpus = { workspace = true }
lru_cache = { workspace = true }
if-addrs = "0.6.4" if-addrs = "0.6.4"
strum = "0.24.0" strum = { workspace = true }
tokio-util = { version = "0.6.3", features = ["time"] } tokio-util = { workspace = true }
derivative = "2.2.0" derivative = { workspace = true }
delay_map = "0.3.0" delay_map = { workspace = true }
ethereum-types = { version = "0.14.1", optional = true } ethereum-types = { workspace = true }
operation_pool = { path = "../operation_pool" } operation_pool = { workspace = true }
execution_layer = { path = "../execution_layer" } execution_layer = { workspace = true }
beacon_processor = { path = "../beacon_processor" } beacon_processor = { workspace = true }
parking_lot = "0.12.0" parking_lot = { workspace = true }
environment = { path = "../../lighthouse/environment" } environment = { workspace = true }
[features] [features]
# NOTE: This can be run via cargo build --bin lighthouse --features network/disable-backfill # NOTE: This can be run via cargo build --bin lighthouse --features network/disable-backfill

View File

@ -12,20 +12,49 @@ use types::EthSpec;
/// Configuration required to construct the UPnP port mappings. /// Configuration required to construct the UPnP port mappings.
pub struct UPnPConfig { pub struct UPnPConfig {
/// The local tcp port. /// The local TCP port.
tcp_port: u16, tcp_port: u16,
/// The local udp port. /// The local UDP discovery port.
udp_port: u16, disc_port: u16,
/// The local UDP quic port.
quic_port: u16,
/// Whether discovery is enabled or not. /// Whether discovery is enabled or not.
disable_discovery: bool, disable_discovery: bool,
/// Whether quic is enabled or not.
disable_quic_support: bool,
}
/// Contains mappings that managed to be established.
#[derive(Default, Debug)]
pub struct EstablishedUPnPMappings {
/// A TCP port mapping for libp2p.
pub tcp_port: Option<u16>,
/// A UDP port for the QUIC libp2p transport.
pub udp_quic_port: Option<u16>,
/// A UDP port for discv5.
pub udp_disc_port: Option<u16>,
}
impl EstablishedUPnPMappings {
/// Returns true if at least one value is set.
pub fn is_some(&self) -> bool {
self.tcp_port.is_some() || self.udp_quic_port.is_some() || self.udp_disc_port.is_some()
}
// Iterator over the UDP ports
pub fn udp_ports(&self) -> impl Iterator<Item = &u16> {
self.udp_quic_port.iter().chain(self.udp_disc_port.iter())
}
} }
impl UPnPConfig { impl UPnPConfig {
pub fn from_config(config: &NetworkConfig) -> Option<Self> { pub fn from_config(config: &NetworkConfig) -> Option<Self> {
config.listen_addrs().v4().map(|v4_addr| UPnPConfig { config.listen_addrs().v4().map(|v4_addr| UPnPConfig {
tcp_port: v4_addr.tcp_port, tcp_port: v4_addr.tcp_port,
udp_port: v4_addr.udp_port, disc_port: v4_addr.disc_port,
quic_port: v4_addr.quic_port,
disable_discovery: config.disable_discovery, disable_discovery: config.disable_discovery,
disable_quic_support: config.disable_quic_support,
}) })
} }
} }
@ -68,6 +97,8 @@ pub fn construct_upnp_mappings<T: EthSpec>(
debug!(log, "UPnP Local IP Discovered"; "ip" => ?local_ip); debug!(log, "UPnP Local IP Discovered"; "ip" => ?local_ip);
let mut mappings = EstablishedUPnPMappings::default();
match local_ip { match local_ip {
IpAddr::V4(address) => { IpAddr::V4(address) => {
let libp2p_socket = SocketAddrV4::new(address, config.tcp_port); let libp2p_socket = SocketAddrV4::new(address, config.tcp_port);
@ -76,39 +107,46 @@ pub fn construct_upnp_mappings<T: EthSpec>(
// one. // one.
// I've found this to be more reliable. If multiple users are behind a single // I've found this to be more reliable. If multiple users are behind a single
// router, they should ideally try to set different port numbers. // router, they should ideally try to set different port numbers.
let tcp_socket = add_port_mapping( mappings.tcp_port = add_port_mapping(
&gateway, &gateway,
igd::PortMappingProtocol::TCP, igd::PortMappingProtocol::TCP,
libp2p_socket, libp2p_socket,
"tcp", "tcp",
&log, &log,
).and_then(|_| { ).map(|_| {
let external_socket = external_ip.as_ref().map(|ip| SocketAddr::new((*ip).into(), config.tcp_port)).map_err(|_| ()); let external_socket = external_ip.as_ref().map(|ip| SocketAddr::new((*ip).into(), config.tcp_port)).map_err(|_| ());
info!(log, "UPnP TCP route established"; "external_socket" => format!("{}:{}", external_socket.as_ref().map(|ip| ip.to_string()).unwrap_or_else(|_| "".into()), config.tcp_port)); info!(log, "UPnP TCP route established"; "external_socket" => format!("{}:{}", external_socket.as_ref().map(|ip| ip.to_string()).unwrap_or_else(|_| "".into()), config.tcp_port));
external_socket config.tcp_port
}).ok(); }).ok();
let udp_socket = if !config.disable_discovery { let set_udp_mapping = |udp_port| {
let discovery_socket = SocketAddrV4::new(address, config.udp_port); let udp_socket = SocketAddrV4::new(address, udp_port);
add_port_mapping( add_port_mapping(
&gateway, &gateway,
igd::PortMappingProtocol::UDP, igd::PortMappingProtocol::UDP,
discovery_socket, udp_socket,
"udp", "udp",
&log, &log,
).and_then(|_| { ).map(|_| {
let external_socket = external_ip info!(log, "UPnP UDP route established"; "external_socket" => format!("{}:{}", external_ip.as_ref().map(|ip| ip.to_string()).unwrap_or_else(|_| "".into()), udp_port));
.map(|ip| SocketAddr::new(ip.into(), config.udp_port)).map_err(|_| ()); })
info!(log, "UPnP UDP route established"; "external_socket" => format!("{}:{}", external_socket.as_ref().map(|ip| ip.to_string()).unwrap_or_else(|_| "".into()), config.udp_port));
external_socket
}).ok()
} else {
None
}; };
// Set the discovery UDP port mapping
if !config.disable_discovery && set_udp_mapping(config.disc_port).is_ok() {
mappings.udp_disc_port = Some(config.disc_port);
}
// Set the quic UDP port mapping
if !config.disable_quic_support && set_udp_mapping(config.quic_port).is_ok() {
mappings.udp_quic_port = Some(config.quic_port);
}
// report any updates to the network service. // report any updates to the network service.
network_send.send(NetworkMessage::UPnPMappingEstablished{ tcp_socket, udp_socket }) if mappings.is_some() {
.unwrap_or_else(|e| debug!(log, "Could not send message to the network service"; "error" => %e)); network_send.send(NetworkMessage::UPnPMappingEstablished{ mappings })
.unwrap_or_else(|e| debug!(log, "Could not send message to the network service"; "error" => %e));
}
} }
_ => debug!(log, "UPnP no routes constructed. IPv6 not supported"), _ => debug!(log, "UPnP no routes constructed. IPv6 not supported"),
} }
@ -161,12 +199,12 @@ fn add_port_mapping(
} }
/// Removes the specified TCP and UDP port mappings. /// Removes the specified TCP and UDP port mappings.
pub fn remove_mappings(tcp_port: Option<u16>, udp_port: Option<u16>, log: &slog::Logger) { pub fn remove_mappings(mappings: &EstablishedUPnPMappings, log: &slog::Logger) {
if tcp_port.is_some() || udp_port.is_some() { if mappings.is_some() {
debug!(log, "Removing UPnP port mappings"); debug!(log, "Removing UPnP port mappings");
match igd::search_gateway(Default::default()) { match igd::search_gateway(Default::default()) {
Ok(gateway) => { Ok(gateway) => {
if let Some(tcp_port) = tcp_port { if let Some(tcp_port) = mappings.tcp_port {
match gateway.remove_port(igd::PortMappingProtocol::TCP, tcp_port) { match gateway.remove_port(igd::PortMappingProtocol::TCP, tcp_port) {
Ok(()) => debug!(log, "UPnP Removed TCP port mapping"; "port" => tcp_port), Ok(()) => debug!(log, "UPnP Removed TCP port mapping"; "port" => tcp_port),
Err(e) => { Err(e) => {
@ -174,8 +212,8 @@ pub fn remove_mappings(tcp_port: Option<u16>, udp_port: Option<u16>, log: &slog:
} }
} }
} }
if let Some(udp_port) = udp_port { for udp_port in mappings.udp_ports() {
match gateway.remove_port(igd::PortMappingProtocol::UDP, udp_port) { match gateway.remove_port(igd::PortMappingProtocol::UDP, *udp_port) {
Ok(()) => debug!(log, "UPnP Removed UDP port mapping"; "port" => udp_port), Ok(()) => debug!(log, "UPnP Removed UDP port mapping"; "port" => udp_port),
Err(e) => { Err(e) => {
debug!(log, "UPnP Failed to remove UDP port mapping"; "port" => udp_port, "error" => %e) debug!(log, "UPnP Failed to remove UDP port mapping"; "port" => udp_port, "error" => %e)

View File

@ -42,7 +42,6 @@ const VALIDATOR_COUNT: usize = SLOTS_PER_EPOCH as usize;
const SMALL_CHAIN: u64 = 2; const SMALL_CHAIN: u64 = 2;
const LONG_CHAIN: u64 = SLOTS_PER_EPOCH * 2; const LONG_CHAIN: u64 = SLOTS_PER_EPOCH * 2;
const TCP_PORT: u16 = 42;
const SEQ_NUMBER: u64 = 0; const SEQ_NUMBER: u64 = 0;
/// The default time to wait for `BeaconProcessor` events. /// The default time to wait for `BeaconProcessor` events.
@ -202,15 +201,7 @@ impl TestRig {
}); });
let enr_key = CombinedKey::generate_secp256k1(); let enr_key = CombinedKey::generate_secp256k1();
let enr = EnrBuilder::new("v4").build(&enr_key).unwrap(); let enr = EnrBuilder::new("v4").build(&enr_key).unwrap();
let network_globals = Arc::new(NetworkGlobals::new( let network_globals = Arc::new(NetworkGlobals::new(enr, meta_data, vec![], false, &log));
enr,
Some(TCP_PORT),
None,
meta_data,
vec![],
false,
&log,
));
let executor = harness.runtime.task_executor.clone(); let executor = harness.runtime.task_executor.clone();

View File

@ -1,4 +1,5 @@
use super::sync::manager::RequestId as SyncId; use super::sync::manager::RequestId as SyncId;
use crate::nat::EstablishedUPnPMappings;
use crate::network_beacon_processor::InvalidBlockStorage; use crate::network_beacon_processor::InvalidBlockStorage;
use crate::persisted_dht::{clear_dht, load_dht, persist_dht}; use crate::persisted_dht::{clear_dht, load_dht, persist_dht};
use crate::router::{Router, RouterMessage}; use crate::router::{Router, RouterMessage};
@ -26,7 +27,7 @@ use lighthouse_network::{
MessageId, NetworkEvent, NetworkGlobals, PeerId, MessageId, NetworkEvent, NetworkGlobals, PeerId,
}; };
use slog::{crit, debug, error, info, o, trace, warn}; use slog::{crit, debug, error, info, o, trace, warn};
use std::{collections::HashSet, net::SocketAddr, pin::Pin, sync::Arc, time::Duration}; use std::{collections::HashSet, pin::Pin, sync::Arc, time::Duration};
use store::HotColdDB; use store::HotColdDB;
use strum::IntoStaticStr; use strum::IntoStaticStr;
use task_executor::ShutdownReason; use task_executor::ShutdownReason;
@ -93,12 +94,10 @@ pub enum NetworkMessage<T: EthSpec> {
/// The result of the validation /// The result of the validation
validation_result: MessageAcceptance, validation_result: MessageAcceptance,
}, },
/// Called if a known external TCP socket address has been updated. /// Called if UPnP managed to establish an external port mapping.
UPnPMappingEstablished { UPnPMappingEstablished {
/// The external TCP address has been updated. /// The mappings that were established.
tcp_socket: Option<SocketAddr>, mappings: EstablishedUPnPMappings,
/// The external UDP address has been updated.
udp_socket: Option<SocketAddr>,
}, },
/// Reports a peer to the peer manager for performing an action. /// Reports a peer to the peer manager for performing an action.
ReportPeer { ReportPeer {
@ -190,11 +189,8 @@ pub struct NetworkService<T: BeaconChainTypes> {
/// A collection of global variables, accessible outside of the network service. /// A collection of global variables, accessible outside of the network service.
network_globals: Arc<NetworkGlobals<T::EthSpec>>, network_globals: Arc<NetworkGlobals<T::EthSpec>>,
/// Stores potentially created UPnP mappings to be removed on shutdown. (TCP port and UDP /// Stores potentially created UPnP mappings to be removed on shutdown. (TCP port and UDP
/// port). /// ports).
upnp_mappings: (Option<u16>, Option<u16>), upnp_mappings: EstablishedUPnPMappings,
/// Keeps track of if discovery is auto-updating or not. This is used to inform us if we should
/// update the UDP socket of discovery if the UPnP mappings get established.
discovery_auto_update: bool,
/// A delay that expires when a new fork takes place. /// A delay that expires when a new fork takes place.
next_fork_update: Pin<Box<OptionFuture<Sleep>>>, next_fork_update: Pin<Box<OptionFuture<Sleep>>>,
/// A delay that expires when we need to subscribe to a new fork's topics. /// A delay that expires when we need to subscribe to a new fork's topics.
@ -359,8 +355,7 @@ impl<T: BeaconChainTypes> NetworkService<T> {
router_send, router_send,
store, store,
network_globals: network_globals.clone(), network_globals: network_globals.clone(),
upnp_mappings: (None, None), upnp_mappings: EstablishedUPnPMappings::default(),
discovery_auto_update: config.discv5_config.enr_update,
next_fork_update, next_fork_update,
next_fork_subscriptions, next_fork_subscriptions,
next_unsubscribe, next_unsubscribe,
@ -617,32 +612,18 @@ impl<T: BeaconChainTypes> NetworkService<T> {
} => { } => {
self.libp2p.send_error_reponse(peer_id, id, error, reason); self.libp2p.send_error_reponse(peer_id, id, error, reason);
} }
NetworkMessage::UPnPMappingEstablished { NetworkMessage::UPnPMappingEstablished { mappings } => {
tcp_socket, self.upnp_mappings = mappings;
udp_socket,
} => {
self.upnp_mappings = (tcp_socket.map(|s| s.port()), udp_socket.map(|s| s.port()));
// If there is an external TCP port update, modify our local ENR. // If there is an external TCP port update, modify our local ENR.
if let Some(tcp_socket) = tcp_socket { if let Some(tcp_port) = self.upnp_mappings.tcp_port {
if let Err(e) = self if let Err(e) = self.libp2p.discovery_mut().update_enr_tcp_port(tcp_port) {
.libp2p
.discovery_mut()
.update_enr_tcp_port(tcp_socket.port())
{
warn!(self.log, "Failed to update ENR"; "error" => e); warn!(self.log, "Failed to update ENR"; "error" => e);
} }
} }
// if the discovery service is not auto-updating, update it with the // If there is an external QUIC port update, modify our local ENR.
// UPnP mappings if let Some(quic_port) = self.upnp_mappings.udp_quic_port {
if !self.discovery_auto_update { if let Err(e) = self.libp2p.discovery_mut().update_enr_quic_port(quic_port) {
if let Some(udp_socket) = udp_socket { warn!(self.log, "Failed to update ENR"; "error" => e);
if let Err(e) = self
.libp2p
.discovery_mut()
.update_enr_udp_socket(udp_socket)
{
warn!(self.log, "Failed to update ENR"; "error" => e);
}
} }
} }
} }
@ -997,7 +978,7 @@ impl<T: BeaconChainTypes> Drop for NetworkService<T> {
} }
// attempt to remove port mappings // attempt to remove port mappings
crate::nat::remove_mappings(self.upnp_mappings.0, self.upnp_mappings.1, &self.log); crate::nat::remove_mappings(&self.upnp_mappings, &self.log);
info!(self.log, "Network service shutdown"); info!(self.log, "Network service shutdown");
} }

View File

@ -60,7 +60,7 @@ mod tests {
); );
let mut config = NetworkConfig::default(); let mut config = NetworkConfig::default();
config.set_ipv4_listening_address(std::net::Ipv4Addr::UNSPECIFIED, 21212, 21212); config.set_ipv4_listening_address(std::net::Ipv4Addr::UNSPECIFIED, 21212, 21212, 21213);
config.discv5_config.table_filter = |_| true; // Do not ignore local IPs config.discv5_config.table_filter = |_| true; // Do not ignore local IPs
config.upnp_enabled = false; config.upnp_enabled = false;
config.boot_nodes_enr = enrs.clone(); config.boot_nodes_enr = enrs.clone();

View File

@ -2,26 +2,26 @@
name = "operation_pool" name = "operation_pool"
version = "0.2.0" version = "0.2.0"
authors = ["Michael Sproul <michael@sigmaprime.io>"] authors = ["Michael Sproul <michael@sigmaprime.io>"]
edition = "2021" edition = { workspace = true }
[dependencies] [dependencies]
derivative = "2.1.1" derivative = { workspace = true }
itertools = "0.10.0" itertools = { workspace = true }
lazy_static = "1.4.0" lazy_static = { workspace = true }
lighthouse_metrics = { path = "../../common/lighthouse_metrics" } lighthouse_metrics = { workspace = true }
parking_lot = "0.12.0" parking_lot = { workspace = true }
types = { path = "../../consensus/types" } types = { workspace = true }
state_processing = { path = "../../consensus/state_processing" } state_processing = { workspace = true }
ethereum_ssz = "0.5.0" ethereum_ssz = { workspace = true }
ethereum_ssz_derive = "0.5.3" ethereum_ssz_derive = { workspace = true }
rayon = "1.5.0" rayon = { workspace = true }
serde = "1.0.116" serde = { workspace = true }
serde_derive = "1.0.116" serde_derive = "1.0.116"
store = { path = "../store" } store = { workspace = true }
bitvec = "1" bitvec = { workspace = true }
rand = "0.8.5" rand = { workspace = true }
[dev-dependencies] [dev-dependencies]
beacon_chain = { path = "../beacon_chain" } beacon_chain = { workspace = true }
tokio = { version = "1.14.0", features = ["rt-multi-thread"] } tokio = { workspace = true }
maplit = "1.0.2" maplit = { workspace = true }

View File

@ -1,4 +1,4 @@
use clap::{App, Arg}; use clap::{App, Arg, ArgGroup};
use strum::VariantNames; use strum::VariantNames;
use types::ProgressiveBalancesMode; use types::ProgressiveBalancesMode;
@ -82,11 +82,11 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
.help("The address lighthouse will listen for UDP and TCP connections. To listen \ .help("The address lighthouse will listen for UDP and TCP connections. To listen \
over IpV4 and IpV6 set this flag twice with the different values.\n\ over IpV4 and IpV6 set this flag twice with the different values.\n\
Examples:\n\ Examples:\n\
- --listen-address '0.0.0.0' will listen over Ipv4.\n\ - --listen-address '0.0.0.0' will listen over IPv4.\n\
- --listen-address '::' will listen over Ipv6.\n\ - --listen-address '::' will listen over IPv6.\n\
- --listen-address '0.0.0.0' --listen-address '::' will listen over both \ - --listen-address '0.0.0.0' --listen-address '::' will listen over both \
Ipv4 and Ipv6. The order of the given addresses is not relevant. However, \ IPv4 and IPv6. The order of the given addresses is not relevant. However, \
multiple Ipv4, or multiple Ipv6 addresses will not be accepted.") multiple IPv4, or multiple IPv6 addresses will not be accepted.")
.multiple(true) .multiple(true)
.max_values(2) .max_values(2)
.default_value("0.0.0.0") .default_value("0.0.0.0")
@ -96,9 +96,10 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
Arg::with_name("port") Arg::with_name("port")
.long("port") .long("port")
.value_name("PORT") .value_name("PORT")
.help("The TCP/UDP port to listen on. The UDP port can be modified by the \ .help("The TCP/UDP ports to listen on. There are two UDP ports. \
--discovery-port flag. If listening over both Ipv4 and Ipv6 the --port flag \ The discovery UDP port will be set to this value and the Quic UDP port will be set to this value + 1. The discovery port can be modified by the \
will apply to the Ipv4 address and --port6 to the Ipv6 address.") --discovery-port flag and the quic port can be modified by the --quic-port flag. If listening over both IPv4 and IPv6 the --port flag \
will apply to the IPv4 address and --port6 to the IPv6 address.")
.default_value("9000") .default_value("9000")
.takes_value(true), .takes_value(true),
) )
@ -106,8 +107,8 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
Arg::with_name("port6") Arg::with_name("port6")
.long("port6") .long("port6")
.value_name("PORT") .value_name("PORT")
.help("The TCP/UDP port to listen on over IpV6 when listening over both Ipv4 and \ .help("The TCP/UDP ports to listen on over IPv6 when listening over both IPv4 and \
Ipv6. Defaults to 9090 when required.") IPv6. Defaults to 9090 when required. The Quic UDP port will be set to this value + 1.")
.default_value("9090") .default_value("9090")
.takes_value(true), .takes_value(true),
) )
@ -118,12 +119,27 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
.help("The UDP port that discovery will listen on. Defaults to `port`") .help("The UDP port that discovery will listen on. Defaults to `port`")
.takes_value(true), .takes_value(true),
) )
.arg(
Arg::with_name("quic-port")
.long("quic-port")
.value_name("PORT")
.help("The UDP port that quic will listen on. Defaults to `port` + 1")
.takes_value(true),
)
.arg( .arg(
Arg::with_name("discovery-port6") Arg::with_name("discovery-port6")
.long("discovery-port6") .long("discovery-port6")
.value_name("PORT") .value_name("PORT")
.help("The UDP port that discovery will listen on over IpV6 if listening over \ .help("The UDP port that discovery will listen on over IPv6 if listening over \
both Ipv4 and IpV6. Defaults to `port6`") both IPv4 and IPv6. Defaults to `port6`")
.takes_value(true),
)
.arg(
Arg::with_name("quic-port6")
.long("quic-port6")
.value_name("PORT")
.help("The UDP port that quic will listen on over IPv6 if listening over \
both IPv4 and IPv6. Defaults to `port6` + 1")
.takes_value(true), .takes_value(true),
) )
.arg( .arg(
@ -166,7 +182,15 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
.long("enr-udp-port") .long("enr-udp-port")
.value_name("PORT") .value_name("PORT")
.help("The UDP4 port of the local ENR. Set this only if you are sure other nodes \ .help("The UDP4 port of the local ENR. Set this only if you are sure other nodes \
can connect to your local node on this port over IpV4.") can connect to your local node on this port over IPv4.")
.takes_value(true),
)
.arg(
Arg::with_name("enr-quic-port")
.long("enr-quic-port")
.value_name("PORT")
.help("The quic UDP4 port that will be set on the local ENR. Set this only if you are sure other nodes \
can connect to your local node on this port over IPv4.")
.takes_value(true), .takes_value(true),
) )
.arg( .arg(
@ -174,7 +198,15 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
.long("enr-udp6-port") .long("enr-udp6-port")
.value_name("PORT") .value_name("PORT")
.help("The UDP6 port of the local ENR. Set this only if you are sure other nodes \ .help("The UDP6 port of the local ENR. Set this only if you are sure other nodes \
can connect to your local node on this port over IpV6.") can connect to your local node on this port over IPv6.")
.takes_value(true),
)
.arg(
Arg::with_name("enr-quic6-port")
.long("enr-quic6-port")
.value_name("PORT")
.help("The quic UDP6 port that will be set on the local ENR. Set this only if you are sure other nodes \
can connect to your local node on this port over IPv6.")
.takes_value(true), .takes_value(true),
) )
.arg( .arg(
@ -182,7 +214,7 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
.long("enr-tcp-port") .long("enr-tcp-port")
.value_name("PORT") .value_name("PORT")
.help("The TCP4 port of the local ENR. Set this only if you are sure other nodes \ .help("The TCP4 port of the local ENR. Set this only if you are sure other nodes \
can connect to your local node on this port over IpV4. The --port flag is \ can connect to your local node on this port over IPv4. The --port flag is \
used if this is not set.") used if this is not set.")
.takes_value(true), .takes_value(true),
) )
@ -191,7 +223,7 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
.long("enr-tcp6-port") .long("enr-tcp6-port")
.value_name("PORT") .value_name("PORT")
.help("The TCP6 port of the local ENR. Set this only if you are sure other nodes \ .help("The TCP6 port of the local ENR. Set this only if you are sure other nodes \
can connect to your local node on this port over IpV6. The --port6 flag is \ can connect to your local node on this port over IPv6. The --port6 flag is \
used if this is not set.") used if this is not set.")
.takes_value(true), .takes_value(true),
) )
@ -232,11 +264,18 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
without an ENR.") without an ENR.")
.takes_value(true), .takes_value(true),
) )
// NOTE: This is hidden because it is primarily a developer feature for testnets and
// debugging. We remove it from the list to avoid clutter.
.arg( .arg(
Arg::with_name("disable-discovery") Arg::with_name("disable-discovery")
.long("disable-discovery") .long("disable-discovery")
.help("Disables the discv5 discovery protocol. The node will not search for new peers or participate in the discovery protocol.") .help("Disables the discv5 discovery protocol. The node will not search for new peers or participate in the discovery protocol.")
.takes_value(false), .hidden(true)
)
.arg(
Arg::with_name("disable-quic")
.long("disable-quic")
.help("Disables the quic transport. The node will rely solely on the TCP transport for libp2p connections.")
) )
.arg( .arg(
Arg::with_name("disable-peer-scoring") Arg::with_name("disable-peer-scoring")
@ -323,22 +362,25 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
.arg( .arg(
Arg::with_name("http-address") Arg::with_name("http-address")
.long("http-address") .long("http-address")
.requires("enable_http")
.value_name("ADDRESS") .value_name("ADDRESS")
.help("Set the listen address for the RESTful HTTP API server.") .help("Set the listen address for the RESTful HTTP API server.")
.default_value("127.0.0.1") .default_value_if("enable_http", None, "127.0.0.1")
.takes_value(true), .takes_value(true),
) )
.arg( .arg(
Arg::with_name("http-port") Arg::with_name("http-port")
.long("http-port") .long("http-port")
.requires("enable_http")
.value_name("PORT") .value_name("PORT")
.help("Set the listen TCP port for the RESTful HTTP API server.") .help("Set the listen TCP port for the RESTful HTTP API server.")
.default_value("5052") .default_value_if("enable_http", None, "5052")
.takes_value(true), .takes_value(true),
) )
.arg( .arg(
Arg::with_name("http-allow-origin") Arg::with_name("http-allow-origin")
.long("http-allow-origin") .long("http-allow-origin")
.requires("enable_http")
.value_name("ORIGIN") .value_name("ORIGIN")
.help("Set the value of the Access-Control-Allow-Origin response HTTP header. \ .help("Set the value of the Access-Control-Allow-Origin response HTTP header. \
Use * to allow any origin (not recommended in production). \ Use * to allow any origin (not recommended in production). \
@ -349,11 +391,13 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
.arg( .arg(
Arg::with_name("http-disable-legacy-spec") Arg::with_name("http-disable-legacy-spec")
.long("http-disable-legacy-spec") .long("http-disable-legacy-spec")
.requires("enable_http")
.hidden(true) .hidden(true)
) )
.arg( .arg(
Arg::with_name("http-spec-fork") Arg::with_name("http-spec-fork")
.long("http-spec-fork") .long("http-spec-fork")
.requires("enable_http")
.value_name("FORK") .value_name("FORK")
.help("Serve the spec for a specific hard fork on /eth/v1/config/spec. It should \ .help("Serve the spec for a specific hard fork on /eth/v1/config/spec. It should \
not be necessary to set this flag.") not be necessary to set this flag.")
@ -371,6 +415,7 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
.arg( .arg(
Arg::with_name("http-tls-cert") Arg::with_name("http-tls-cert")
.long("http-tls-cert") .long("http-tls-cert")
.requires("enable_http")
.help("The path of the certificate to be used when serving the HTTP API server \ .help("The path of the certificate to be used when serving the HTTP API server \
over TLS.") over TLS.")
.takes_value(true) .takes_value(true)
@ -378,6 +423,7 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
.arg( .arg(
Arg::with_name("http-tls-key") Arg::with_name("http-tls-key")
.long("http-tls-key") .long("http-tls-key")
.requires("enable_http")
.help("The path of the private key to be used when serving the HTTP API server \ .help("The path of the private key to be used when serving the HTTP API server \
over TLS. Must not be password-protected.") over TLS. Must not be password-protected.")
.takes_value(true) .takes_value(true)
@ -385,6 +431,7 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
.arg( .arg(
Arg::with_name("http-allow-sync-stalled") Arg::with_name("http-allow-sync-stalled")
.long("http-allow-sync-stalled") .long("http-allow-sync-stalled")
.requires("enable_http")
.help("Forces the HTTP to indicate that the node is synced when sync is actually \ .help("Forces the HTTP to indicate that the node is synced when sync is actually \
stalled. This is useful for very small testnets. TESTING ONLY. DO NOT USE ON \ stalled. This is useful for very small testnets. TESTING ONLY. DO NOT USE ON \
MAINNET.") MAINNET.")
@ -392,8 +439,9 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
.arg( .arg(
Arg::with_name("http-sse-capacity-multiplier") Arg::with_name("http-sse-capacity-multiplier")
.long("http-sse-capacity-multiplier") .long("http-sse-capacity-multiplier")
.requires("enable_http")
.takes_value(true) .takes_value(true)
.default_value("1") .default_value_if("enable_http", None, "1")
.value_name("N") .value_name("N")
.help("Multiplier to apply to the length of HTTP server-sent-event (SSE) channels. \ .help("Multiplier to apply to the length of HTTP server-sent-event (SSE) channels. \
Increasing this value can prevent messages from being dropped.") Increasing this value can prevent messages from being dropped.")
@ -401,8 +449,9 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
.arg( .arg(
Arg::with_name("http-duplicate-block-status") Arg::with_name("http-duplicate-block-status")
.long("http-duplicate-block-status") .long("http-duplicate-block-status")
.requires("enable_http")
.takes_value(true) .takes_value(true)
.default_value("202") .default_value_if("enable_http", None, "202")
.value_name("STATUS_CODE") .value_name("STATUS_CODE")
.help("Status code to send when a block that is already known is POSTed to the \ .help("Status code to send when a block that is already known is POSTed to the \
HTTP API.") HTTP API.")
@ -410,13 +459,14 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
.arg( .arg(
Arg::with_name("http-enable-beacon-processor") Arg::with_name("http-enable-beacon-processor")
.long("http-enable-beacon-processor") .long("http-enable-beacon-processor")
.requires("enable_http")
.value_name("BOOLEAN") .value_name("BOOLEAN")
.help("The beacon processor is a scheduler which provides quality-of-service and \ .help("The beacon processor is a scheduler which provides quality-of-service and \
DoS protection. When set to \"true\", HTTP API requests will be queued and scheduled \ DoS protection. When set to \"true\", HTTP API requests will be queued and scheduled \
alongside other tasks. When set to \"false\", HTTP API responses will be executed \ alongside other tasks. When set to \"false\", HTTP API responses will be executed \
immediately.") immediately.")
.takes_value(true) .takes_value(true)
.default_value("true") .default_value_if("enable_http", None, "true")
) )
/* Prometheus metrics HTTP server related arguments */ /* Prometheus metrics HTTP server related arguments */
.arg( .arg(
@ -429,22 +479,25 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
Arg::with_name("metrics-address") Arg::with_name("metrics-address")
.long("metrics-address") .long("metrics-address")
.value_name("ADDRESS") .value_name("ADDRESS")
.requires("metrics")
.help("Set the listen address for the Prometheus metrics HTTP server.") .help("Set the listen address for the Prometheus metrics HTTP server.")
.default_value("127.0.0.1") .default_value_if("metrics", None, "127.0.0.1")
.takes_value(true), .takes_value(true),
) )
.arg( .arg(
Arg::with_name("metrics-port") Arg::with_name("metrics-port")
.long("metrics-port") .long("metrics-port")
.requires("metrics")
.value_name("PORT") .value_name("PORT")
.help("Set the listen TCP port for the Prometheus metrics HTTP server.") .help("Set the listen TCP port for the Prometheus metrics HTTP server.")
.default_value("5054") .default_value_if("metrics", None, "5054")
.takes_value(true), .takes_value(true),
) )
.arg( .arg(
Arg::with_name("metrics-allow-origin") Arg::with_name("metrics-allow-origin")
.long("metrics-allow-origin") .long("metrics-allow-origin")
.value_name("ORIGIN") .value_name("ORIGIN")
.requires("metrics")
.help("Set the value of the Access-Control-Allow-Origin response HTTP header. \ .help("Set the value of the Access-Control-Allow-Origin response HTTP header. \
Use * to allow any origin (not recommended in production). \ Use * to allow any origin (not recommended in production). \
If no value is supplied, the CORS allowed origin is set to the listen \ If no value is supplied, the CORS allowed origin is set to the listen \
@ -1283,4 +1336,5 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
.default_value("64") .default_value("64")
.takes_value(true) .takes_value(true)
) )
.group(ArgGroup::with_name("enable_http").args(&["http", "gui", "staking"]).multiple(true))
} }

View File

@ -95,70 +95,70 @@ pub fn get_config<E: EthSpec>(
* Http API server * Http API server
*/ */
if cli_args.is_present("http") { if cli_args.is_present("enable_http") {
client_config.http_api.enabled = true; client_config.http_api.enabled = true;
if let Some(address) = cli_args.value_of("http-address") {
client_config.http_api.listen_addr = address
.parse::<IpAddr>()
.map_err(|_| "http-address is not a valid IP address.")?;
}
if let Some(port) = cli_args.value_of("http-port") {
client_config.http_api.listen_port = port
.parse::<u16>()
.map_err(|_| "http-port is not a valid u16.")?;
}
if let Some(allow_origin) = cli_args.value_of("http-allow-origin") {
// Pre-validate the config value to give feedback to the user on node startup, instead of
// as late as when the first API response is produced.
hyper::header::HeaderValue::from_str(allow_origin)
.map_err(|_| "Invalid allow-origin value")?;
client_config.http_api.allow_origin = Some(allow_origin.to_string());
}
if cli_args.is_present("http-disable-legacy-spec") {
warn!(
log,
"The flag --http-disable-legacy-spec is deprecated and will be removed"
);
}
if let Some(fork_name) = clap_utils::parse_optional(cli_args, "http-spec-fork")? {
client_config.http_api.spec_fork_name = Some(fork_name);
}
if cli_args.is_present("http-enable-tls") {
client_config.http_api.tls_config = Some(TlsConfig {
cert: cli_args
.value_of("http-tls-cert")
.ok_or("--http-tls-cert was not provided.")?
.parse::<PathBuf>()
.map_err(|_| "http-tls-cert is not a valid path name.")?,
key: cli_args
.value_of("http-tls-key")
.ok_or("--http-tls-key was not provided.")?
.parse::<PathBuf>()
.map_err(|_| "http-tls-key is not a valid path name.")?,
});
}
if cli_args.is_present("http-allow-sync-stalled") {
client_config.http_api.allow_sync_stalled = true;
}
client_config.http_api.sse_capacity_multiplier =
parse_required(cli_args, "http-sse-capacity-multiplier")?;
client_config.http_api.enable_beacon_processor =
parse_required(cli_args, "http-enable-beacon-processor")?;
client_config.http_api.duplicate_block_status_code =
parse_required(cli_args, "http-duplicate-block-status")?;
} }
if let Some(address) = cli_args.value_of("http-address") {
client_config.http_api.listen_addr = address
.parse::<IpAddr>()
.map_err(|_| "http-address is not a valid IP address.")?;
}
if let Some(port) = cli_args.value_of("http-port") {
client_config.http_api.listen_port = port
.parse::<u16>()
.map_err(|_| "http-port is not a valid u16.")?;
}
if let Some(allow_origin) = cli_args.value_of("http-allow-origin") {
// Pre-validate the config value to give feedback to the user on node startup, instead of
// as late as when the first API response is produced.
hyper::header::HeaderValue::from_str(allow_origin)
.map_err(|_| "Invalid allow-origin value")?;
client_config.http_api.allow_origin = Some(allow_origin.to_string());
}
if cli_args.is_present("http-disable-legacy-spec") {
warn!(
log,
"The flag --http-disable-legacy-spec is deprecated and will be removed"
);
}
if let Some(fork_name) = clap_utils::parse_optional(cli_args, "http-spec-fork")? {
client_config.http_api.spec_fork_name = Some(fork_name);
}
if cli_args.is_present("http-enable-tls") {
client_config.http_api.tls_config = Some(TlsConfig {
cert: cli_args
.value_of("http-tls-cert")
.ok_or("--http-tls-cert was not provided.")?
.parse::<PathBuf>()
.map_err(|_| "http-tls-cert is not a valid path name.")?,
key: cli_args
.value_of("http-tls-key")
.ok_or("--http-tls-key was not provided.")?
.parse::<PathBuf>()
.map_err(|_| "http-tls-key is not a valid path name.")?,
});
}
if cli_args.is_present("http-allow-sync-stalled") {
client_config.http_api.allow_sync_stalled = true;
}
client_config.http_api.sse_capacity_multiplier =
parse_required(cli_args, "http-sse-capacity-multiplier")?;
client_config.http_api.enable_beacon_processor =
parse_required(cli_args, "http-enable-beacon-processor")?;
client_config.http_api.duplicate_block_status_code =
parse_required(cli_args, "http-duplicate-block-status")?;
if let Some(cache_size) = clap_utils::parse_optional(cli_args, "shuffling-cache-size")? { if let Some(cache_size) = clap_utils::parse_optional(cli_args, "shuffling-cache-size")? {
client_config.chain.shuffling_cache_size = cache_size; client_config.chain.shuffling_cache_size = cache_size;
} }
@ -952,15 +952,15 @@ pub fn parse_listening_addresses(
.map_err(|parse_error| format!("Failed to parse --port6 as an integer: {parse_error}"))? .map_err(|parse_error| format!("Failed to parse --port6 as an integer: {parse_error}"))?
.unwrap_or(9090); .unwrap_or(9090);
// parse the possible udp ports // parse the possible discovery ports.
let maybe_udp_port = cli_args let maybe_disc_port = cli_args
.value_of("discovery-port") .value_of("discovery-port")
.map(str::parse::<u16>) .map(str::parse::<u16>)
.transpose() .transpose()
.map_err(|parse_error| { .map_err(|parse_error| {
format!("Failed to parse --discovery-port as an integer: {parse_error}") format!("Failed to parse --discovery-port as an integer: {parse_error}")
})?; })?;
let maybe_udp6_port = cli_args let maybe_disc6_port = cli_args
.value_of("discovery-port6") .value_of("discovery-port6")
.map(str::parse::<u16>) .map(str::parse::<u16>)
.transpose() .transpose()
@ -968,6 +968,24 @@ pub fn parse_listening_addresses(
format!("Failed to parse --discovery-port6 as an integer: {parse_error}") format!("Failed to parse --discovery-port6 as an integer: {parse_error}")
})?; })?;
// parse the possible quic port.
let maybe_quic_port = cli_args
.value_of("quic-port")
.map(str::parse::<u16>)
.transpose()
.map_err(|parse_error| {
format!("Failed to parse --quic-port as an integer: {parse_error}")
})?;
// parse the possible quic port.
let maybe_quic6_port = cli_args
.value_of("quic-port6")
.map(str::parse::<u16>)
.transpose()
.map_err(|parse_error| {
format!("Failed to parse --quic6-port as an integer: {parse_error}")
})?;
// Now put everything together // Now put everything together
let listening_addresses = match (maybe_ipv4, maybe_ipv6) { let listening_addresses = match (maybe_ipv4, maybe_ipv6) {
(None, None) => { (None, None) => {
@ -978,7 +996,7 @@ pub fn parse_listening_addresses(
// A single ipv6 address was provided. Set the ports // A single ipv6 address was provided. Set the ports
if cli_args.is_present("port6") { if cli_args.is_present("port6") {
warn!(log, "When listening only over IpV6, use the --port flag. The value of --port6 will be ignored.") warn!(log, "When listening only over IPv6, use the --port flag. The value of --port6 will be ignored.")
} }
// use zero ports if required. If not, use the given port. // use zero ports if required. If not, use the given port.
let tcp_port = use_zero_ports let tcp_port = use_zero_ports
@ -986,20 +1004,32 @@ pub fn parse_listening_addresses(
.transpose()? .transpose()?
.unwrap_or(port); .unwrap_or(port);
if maybe_udp6_port.is_some() { if maybe_disc6_port.is_some() {
warn!(log, "When listening only over IpV6, use the --discovery-port flag. The value of --discovery-port6 will be ignored.") warn!(log, "When listening only over IPv6, use the --discovery-port flag. The value of --discovery-port6 will be ignored.")
} }
if maybe_quic6_port.is_some() {
warn!(log, "When listening only over IPv6, use the --quic-port flag. The value of --quic-port6 will be ignored.")
}
// use zero ports if required. If not, use the specific udp port. If none given, use // use zero ports if required. If not, use the specific udp port. If none given, use
// the tcp port. // the tcp port.
let udp_port = use_zero_ports let disc_port = use_zero_ports
.then(unused_port::unused_udp6_port) .then(unused_port::unused_udp6_port)
.transpose()? .transpose()?
.or(maybe_udp_port) .or(maybe_disc_port)
.unwrap_or(port); .unwrap_or(port);
let quic_port = use_zero_ports
.then(unused_port::unused_udp6_port)
.transpose()?
.or(maybe_quic_port)
.unwrap_or(port + 1);
ListenAddress::V6(lighthouse_network::ListenAddr { ListenAddress::V6(lighthouse_network::ListenAddr {
addr: ipv6, addr: ipv6,
udp_port, quic_port,
disc_port,
tcp_port, tcp_port,
}) })
} }
@ -1011,16 +1041,25 @@ pub fn parse_listening_addresses(
.then(unused_port::unused_tcp4_port) .then(unused_port::unused_tcp4_port)
.transpose()? .transpose()?
.unwrap_or(port); .unwrap_or(port);
// use zero ports if required. If not, use the specific udp port. If none given, use // use zero ports if required. If not, use the specific discovery port. If none given, use
// the tcp port. // the tcp port.
let udp_port = use_zero_ports let disc_port = use_zero_ports
.then(unused_port::unused_udp4_port) .then(unused_port::unused_udp4_port)
.transpose()? .transpose()?
.or(maybe_udp_port) .or(maybe_disc_port)
.unwrap_or(port); .unwrap_or(port);
// use zero ports if required. If not, use the specific quic port. If none given, use
// the tcp port + 1.
let quic_port = use_zero_ports
.then(unused_port::unused_udp4_port)
.transpose()?
.or(maybe_quic_port)
.unwrap_or(port + 1);
ListenAddress::V4(lighthouse_network::ListenAddr { ListenAddress::V4(lighthouse_network::ListenAddr {
addr: ipv4, addr: ipv4,
udp_port, disc_port,
quic_port,
tcp_port, tcp_port,
}) })
} }
@ -1029,31 +1068,44 @@ pub fn parse_listening_addresses(
.then(unused_port::unused_tcp4_port) .then(unused_port::unused_tcp4_port)
.transpose()? .transpose()?
.unwrap_or(port); .unwrap_or(port);
let ipv4_udp_port = use_zero_ports let ipv4_disc_port = use_zero_ports
.then(unused_port::unused_udp4_port) .then(unused_port::unused_udp4_port)
.transpose()? .transpose()?
.or(maybe_udp_port) .or(maybe_disc_port)
.unwrap_or(ipv4_tcp_port); .unwrap_or(ipv4_tcp_port);
let ipv4_quic_port = use_zero_ports
.then(unused_port::unused_udp4_port)
.transpose()?
.or(maybe_quic_port)
.unwrap_or(port + 1);
// Defaults to 9090 when required // Defaults to 9090 when required
let ipv6_tcp_port = use_zero_ports let ipv6_tcp_port = use_zero_ports
.then(unused_port::unused_tcp6_port) .then(unused_port::unused_tcp6_port)
.transpose()? .transpose()?
.unwrap_or(port6); .unwrap_or(port6);
let ipv6_udp_port = use_zero_ports let ipv6_disc_port = use_zero_ports
.then(unused_port::unused_udp6_port) .then(unused_port::unused_udp6_port)
.transpose()? .transpose()?
.or(maybe_udp6_port) .or(maybe_disc6_port)
.unwrap_or(ipv6_tcp_port); .unwrap_or(ipv6_tcp_port);
let ipv6_quic_port = use_zero_ports
.then(unused_port::unused_udp6_port)
.transpose()?
.or(maybe_quic6_port)
.unwrap_or(ipv6_tcp_port + 1);
ListenAddress::DualStack( ListenAddress::DualStack(
lighthouse_network::ListenAddr { lighthouse_network::ListenAddr {
addr: ipv4, addr: ipv4,
udp_port: ipv4_udp_port, disc_port: ipv4_disc_port,
quic_port: ipv4_quic_port,
tcp_port: ipv4_tcp_port, tcp_port: ipv4_tcp_port,
}, },
lighthouse_network::ListenAddr { lighthouse_network::ListenAddr {
addr: ipv6, addr: ipv6,
udp_port: ipv6_udp_port, disc_port: ipv6_disc_port,
quic_port: ipv6_quic_port,
tcp_port: ipv6_tcp_port, tcp_port: ipv6_tcp_port,
}, },
) )
@ -1169,6 +1221,14 @@ pub fn set_network_config(
); );
} }
if let Some(enr_quic_port_str) = cli_args.value_of("enr-quic-port") {
config.enr_quic4_port = Some(
enr_quic_port_str
.parse::<u16>()
.map_err(|_| format!("Invalid 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
@ -1185,6 +1245,14 @@ pub fn set_network_config(
); );
} }
if let Some(enr_quic_port_str) = cli_args.value_of("enr-quic6-port") {
config.enr_quic6_port = Some(
enr_quic_port_str
.parse::<u16>()
.map_err(|_| format!("Invalid 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
@ -1194,9 +1262,9 @@ 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 // 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() {
let ipv4_enr_addr = if ipv4_addr.addr == Ipv4Addr::UNSPECIFIED { let ipv4_enr_addr = if ipv4_addr.addr == Ipv4Addr::UNSPECIFIED {
Ipv4Addr::LOCALHOST Ipv4Addr::LOCALHOST
@ -1204,7 +1272,7 @@ pub fn set_network_config(
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.udp_port); config.enr_udp4_port = Some(ipv4_addr.disc_port);
} }
if let Some(ipv6_addr) = config.listen_addrs().v6().cloned() { if let Some(ipv6_addr) = config.listen_addrs().v6().cloned() {
@ -1214,7 +1282,7 @@ pub fn set_network_config(
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.udp_port); config.enr_udp6_port = Some(ipv6_addr.disc_port);
} }
} }
@ -1247,11 +1315,11 @@ pub fn set_network_config(
// actually matters. Just use the udp port. // actually matters. Just use the udp port.
let port = match config.listen_addrs() { let port = match config.listen_addrs() {
ListenAddress::V4(v4_addr) => v4_addr.udp_port, ListenAddress::V4(v4_addr) => v4_addr.disc_port,
ListenAddress::V6(v6_addr) => v6_addr.udp_port, ListenAddress::V6(v6_addr) => v6_addr.disc_port,
ListenAddress::DualStack(v4_addr, _v6_addr) => { ListenAddress::DualStack(v4_addr, _v6_addr) => {
// NOTE: slight preference for ipv4 that I don't think is of importance. // NOTE: slight preference for ipv4 that I don't think is of importance.
v4_addr.udp_port v4_addr.disc_port
} }
}; };
@ -1310,6 +1378,10 @@ pub fn set_network_config(
warn!(log, "Discovery is disabled. New peers will not be found"); warn!(log, "Discovery is disabled. New peers will not be found");
} }
if cli_args.is_present("disable-quic") {
config.disable_quic_support = true;
}
if cli_args.is_present("disable-upnp") { if cli_args.is_present("disable-upnp") {
config.upnp_enabled = false; config.upnp_enabled = false;
} }

View File

@ -2,27 +2,27 @@
name = "store" name = "store"
version = "0.2.0" version = "0.2.0"
authors = ["Paul Hauner <paul@paulhauner.com>"] authors = ["Paul Hauner <paul@paulhauner.com>"]
edition = "2021" edition = { workspace = true }
[dev-dependencies] [dev-dependencies]
tempfile = "3.1.0" tempfile = { workspace = true }
beacon_chain = {path = "../beacon_chain"} beacon_chain = { workspace = true }
[dependencies] [dependencies]
db-key = "0.0.5" db-key = "0.0.5"
leveldb = { version = "0.8.6" } leveldb = { version = "0.8" }
parking_lot = "0.12.0" parking_lot = { workspace = true }
itertools = "0.10.0" itertools = { workspace = true }
ethereum_ssz = "0.5.0" ethereum_ssz = { workspace = true }
ethereum_ssz_derive = "0.5.3" ethereum_ssz_derive = { workspace = true }
types = { path = "../../consensus/types" } types = { workspace = true }
state_processing = { path = "../../consensus/state_processing" } state_processing = { workspace = true }
slog = "2.5.2" slog = { workspace = true }
serde = "1.0.116" serde = { workspace = true }
serde_derive = "1.0.116" serde_derive = "1.0.116"
lazy_static = "1.4.0" lazy_static = { workspace = true }
lighthouse_metrics = { path = "../../common/lighthouse_metrics" } lighthouse_metrics = { workspace = true }
lru = "0.7.1" lru = { workspace = true }
sloggers = { version = "2.1.1", features = ["json"] } sloggers = { workspace = true }
directory = { path = "../../common/directory" } directory = { workspace = true }
strum = { version = "0.24.0", features = ["derive"] } strum = { workspace = true }

View File

@ -2,11 +2,11 @@
name = "timer" name = "timer"
version = "0.2.0" version = "0.2.0"
authors = ["Sigma Prime <contact@sigmaprime.io>"] authors = ["Sigma Prime <contact@sigmaprime.io>"]
edition = "2021" edition = { workspace = true }
[dependencies] [dependencies]
beacon_chain = { path = "../beacon_chain" } beacon_chain = { workspace = true }
slot_clock = { path = "../../common/slot_clock" } slot_clock = { workspace = true }
tokio = { version = "1.14.0", features = ["full"] } tokio = { workspace = true }
slog = "2.5.2" slog = { workspace = true }
task_executor = { path = "../../common/task_executor" } task_executor = { workspace = true }

View File

@ -56,3 +56,4 @@
* [Contributing](./contributing.md) * [Contributing](./contributing.md)
* [Development Environment](./setup.md) * [Development Environment](./setup.md)
* [FAQs](./faq.md) * [FAQs](./faq.md)
* [Protocol Developers](./developers.md)

51
book/src/developers.md Normal file
View File

@ -0,0 +1,51 @@
# For Protocol Developers
_Documentation for protocol developers._
This section lists Lighthouse-specific decisions that are not strictly spec'd and may be useful for
other protocol developers wishing to interact with lighthouse.
## Custom ENR Fields
Lighthouse currently uses the following ENR fields:
### Ethereum Consensus Specified
| Field | Description |
| ---- | ---- |
| `eth2` | The `ENRForkId` in SSZ bytes specifying which fork the node is on |
| `attnets` | An SSZ bitfield which indicates which of the 64 subnets the node is subscribed to for an extended period of time |
| `syncnets` | An SSZ bitfield which indicates which of the sync committee subnets the node is subscribed to |
### Lighthouse Custom Fields
Lighthouse is currently using the following custom ENR fields.
| Field | Description |
| ---- | ---- |
| `quic` | The UDP port on which the QUIC transport is listening on IPv4 |
| `quic6` | The UDP port on which the QUIC transport is listening on IPv6 |
## Custom RPC Messages
The specification leaves room for implementation-specific errors. Lighthouse uses the following
custom RPC error messages.
### Goodbye Reason Codes
| Code | Message | Description |
| ---- | ---- | ---- |
| 128 | Unable to Verify Network | Teku uses this, so we adopted it. It relates to having a fork mismatch |
| 129 | Too Many Peers | Lighthouse can close a connection because it has reached its peer-limit and pruned excess peers |
| 250 | Bad Score | The node has been dropped due to having a bad peer score |
| 251 | Banned | The peer has been banned and disconnected |
| 252 | Banned IP | The IP the node is connected to us with has been banned |
### Error Codes
| Code | Message | Description |
| ---- | ---- | ---- |
| 139 | Rate Limited | The peer has been rate limited so we return this error as a response |

View File

@ -14,7 +14,7 @@ The additional requirements for developers are:
don't have `anvil` available on your `PATH`. don't have `anvil` available on your `PATH`.
- [`cmake`](https://cmake.org/cmake/help/latest/command/install.html). Used by - [`cmake`](https://cmake.org/cmake/help/latest/command/install.html). Used by
some dependencies. See [`Installation Guide`](./installation.md) for more info. some dependencies. See [`Installation Guide`](./installation.md) for more info.
- [`java 11 runtime`](https://openjdk.java.net/projects/jdk/). 11 is the minimum, - [`java 17 runtime`](https://openjdk.java.net/projects/jdk/). 17 is the minimum,
used by web3signer_tests. used by web3signer_tests.
- [`libpq-dev`](https://www.postgresql.org/docs/devel/libpq.html). Also know as - [`libpq-dev`](https://www.postgresql.org/docs/devel/libpq.html). Also know as
`libpq-devel` on some systems. `libpq-devel` on some systems.

View File

@ -1,27 +1,27 @@
[package] [package]
name = "boot_node" name = "boot_node"
version = "4.4.1" version = "4.5.0"
authors = ["Sigma Prime <contact@sigmaprime.io>"] authors = ["Sigma Prime <contact@sigmaprime.io>"]
edition = "2021" edition = { workspace = true }
[dependencies] [dependencies]
beacon_node = { path = "../beacon_node" } beacon_node = { workspace = true }
clap = "2.33.3" clap = { workspace = true }
clap_utils = { path = "../common/clap_utils" } clap_utils = { workspace = true }
lighthouse_network = { path = "../beacon_node/lighthouse_network" } lighthouse_network = { workspace = true }
types = { path = "../consensus/types" } types = { workspace = true }
ethereum_ssz = "0.5.0" ethereum_ssz = { workspace = true }
slog = "2.5.2" slog = { workspace = true }
tokio = "1.14.0" tokio = { workspace = true }
log = "0.4.11" log = { workspace = true }
slog-term = "2.6.0" slog-term = { workspace = true }
logging = { path = "../common/logging" } logging = { workspace = true }
slog-async = "2.5.0" slog-async = { workspace = true }
slog-scope = "4.3.0" slog-scope = "4.3.0"
slog-stdlog = "4.0.0" slog-stdlog = "4.0.0"
hex = "0.4.2" hex = { workspace = true }
serde = "1.0.116" serde = { workspace = true }
serde_derive = "1.0.116" serde_derive = "1.0.116"
serde_json = "1.0.66" serde_json = { workspace = true }
serde_yaml = "0.8.13" serde_yaml = { workspace = true }
eth2_network_config = { path = "../common/eth2_network_config" } eth2_network_config = { workspace = true }

View File

@ -25,7 +25,7 @@ pub struct BootNodeConfig<T: EthSpec> {
} }
impl<T: EthSpec> BootNodeConfig<T> { impl<T: EthSpec> BootNodeConfig<T> {
pub fn new( pub async fn new(
matches: &ArgMatches<'_>, matches: &ArgMatches<'_>,
eth2_network_config: &Eth2NetworkConfig, eth2_network_config: &Eth2NetworkConfig,
) -> Result<Self, String> { ) -> Result<Self, String> {
@ -58,12 +58,12 @@ impl<T: EthSpec> BootNodeConfig<T> {
set_network_config(&mut network_config, matches, &data_dir, &logger)?; set_network_config(&mut network_config, matches, &data_dir, &logger)?;
// Set the Enr UDP 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( network_config.enr_udp4_port = Some(
network_config network_config
.enr_udp4_port .enr_udp4_port
.unwrap_or(listening_addr_v4.udp_port), .unwrap_or(listening_addr_v4.disc_port),
) )
}; };
@ -71,7 +71,7 @@ impl<T: EthSpec> BootNodeConfig<T> {
network_config.enr_udp6_port = Some( network_config.enr_udp6_port = Some(
network_config network_config
.enr_udp6_port .enr_udp6_port
.unwrap_or(listening_addr_v6.udp_port), .unwrap_or(listening_addr_v6.disc_port),
) )
}; };
@ -99,7 +99,7 @@ impl<T: EthSpec> BootNodeConfig<T> {
if eth2_network_config.genesis_state_is_known() { if eth2_network_config.genesis_state_is_known() {
let genesis_state = eth2_network_config let genesis_state = eth2_network_config
.genesis_state::<T>(genesis_state_url.as_deref(), genesis_state_url_timeout, &logger)? .genesis_state::<T>(genesis_state_url.as_deref(), genesis_state_url_timeout, &logger).await?
.ok_or_else(|| { .ok_or_else(|| {
"The genesis state for this network is not known, this is an unsupported mode" "The genesis state for this network is not known, this is an unsupported mode"
.to_string() .to_string()

View File

@ -7,7 +7,7 @@ mod cli;
pub mod config; pub mod config;
mod server; mod server;
pub use cli::cli_app; pub use cli::cli_app;
use config::{BootNodeConfig, BootNodeConfigSerialization}; use config::BootNodeConfig;
use types::{EthSpec, EthSpecId}; use types::{EthSpec, EthSpecId};
const LOG_CHANNEL_SIZE: usize = 2048; const LOG_CHANNEL_SIZE: usize = 2048;
@ -81,20 +81,13 @@ fn main<T: EthSpec>(
.build() .build()
.map_err(|e| format!("Failed to build runtime: {}", e))?; .map_err(|e| format!("Failed to build runtime: {}", e))?;
// parse the CLI args into a useable config
let config: BootNodeConfig<T> = BootNodeConfig::new(bn_matches, eth2_network_config)?;
// Dump configs if `dump-config` or `dump-chain-config` flags are set
let config_sz = BootNodeConfigSerialization::from_config_ref(&config);
clap_utils::check_dump_configs::<_, T>(
lh_matches,
&config_sz,
&eth2_network_config.chain_spec::<T>()?,
)?;
// Run the boot node // Run the boot node
if !lh_matches.is_present("immediate-shutdown") { runtime.block_on(server::run::<T>(
runtime.block_on(server::run(config, log)); lh_matches,
} bn_matches,
eth2_network_config,
log,
))?;
Ok(()) Ok(())
} }

View File

@ -1,6 +1,9 @@
//! The main bootnode server execution. //! The main bootnode server execution.
use super::BootNodeConfig; use super::BootNodeConfig;
use crate::config::BootNodeConfigSerialization;
use clap::ArgMatches;
use eth2_network_config::Eth2NetworkConfig;
use lighthouse_network::{ use lighthouse_network::{
discv5::{enr::NodeId, Discv5, Discv5Event}, discv5::{enr::NodeId, Discv5, Discv5Event},
EnrExt, Eth2Enr, EnrExt, Eth2Enr,
@ -8,7 +11,27 @@ use lighthouse_network::{
use slog::info; use slog::info;
use types::EthSpec; use types::EthSpec;
pub async fn run<T: EthSpec>(config: BootNodeConfig<T>, log: slog::Logger) { pub async fn run<T: EthSpec>(
lh_matches: &ArgMatches<'_>,
bn_matches: &ArgMatches<'_>,
eth2_network_config: &Eth2NetworkConfig,
log: slog::Logger,
) -> Result<(), String> {
// parse the CLI args into a useable config
let config: BootNodeConfig<T> = BootNodeConfig::new(bn_matches, eth2_network_config).await?;
// Dump configs if `dump-config` or `dump-chain-config` flags are set
let config_sz = BootNodeConfigSerialization::from_config_ref(&config);
clap_utils::check_dump_configs::<_, T>(
lh_matches,
&config_sz,
&eth2_network_config.chain_spec::<T>()?,
)?;
if lh_matches.is_present("immediate-shutdown") {
return Ok(());
}
let BootNodeConfig { let BootNodeConfig {
boot_nodes, boot_nodes,
local_enr, local_enr,
@ -65,8 +88,7 @@ pub async fn run<T: EthSpec>(config: BootNodeConfig<T>, log: slog::Logger) {
// start the server // start the server
if let Err(e) = discv5.start().await { if let Err(e) = discv5.start().await {
slog::crit!(log, "Could not start discv5 server"; "error" => %e); return Err(format!("Could not start discv5 server: {e:?}"));
return;
} }
// if there are peers in the local routing table, establish a session by running a query // if there are peers in the local routing table, establish a session by running a query
@ -82,8 +104,7 @@ pub async fn run<T: EthSpec>(config: BootNodeConfig<T>, log: slog::Logger) {
let mut event_stream = match discv5.event_stream().await { let mut event_stream = match discv5.event_stream().await {
Ok(stream) => stream, Ok(stream) => stream,
Err(e) => { Err(e) => {
slog::crit!(log, "Failed to obtain event stream"; "error" => %e); return Err(format!("Failed to obtain event stream: {e:?}"));
return;
} }
}; };

View File

@ -2,22 +2,22 @@
name = "account_utils" name = "account_utils"
version = "0.1.0" version = "0.1.0"
authors = ["Paul Hauner <paul@paulhauner.com>"] authors = ["Paul Hauner <paul@paulhauner.com>"]
edition = "2021" edition = { workspace = true }
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
rand = "0.8.5" rand = { workspace = true }
eth2_wallet = { path = "../../crypto/eth2_wallet" } eth2_wallet = { workspace = true }
eth2_keystore = { path = "../../crypto/eth2_keystore" } eth2_keystore = { workspace = true }
filesystem = { path = "../filesystem" } filesystem = { workspace = true }
zeroize = { version = "1.4.2", features = ["zeroize_derive"] } zeroize = { workspace = true }
serde = "1.0.116" serde = { workspace = true }
serde_derive = "1.0.116" serde_derive = "1.0.116"
serde_yaml = "0.8.13" serde_yaml = { workspace = true }
slog = { version = "2.5.2", features = ["max_level_trace", "release_max_level_trace"] } slog = { workspace = true }
types = { path = "../../consensus/types" } types = { workspace = true }
validator_dir = { path = "../validator_dir" } validator_dir = { workspace = true }
regex = "1.5.5" regex = { workspace = true }
rpassword = "5.0.0" rpassword = "5.0.0"
directory = { path = "../directory" } directory = { workspace = true }

View File

@ -2,18 +2,18 @@
name = "clap_utils" name = "clap_utils"
version = "0.1.0" version = "0.1.0"
authors = ["Paul Hauner <paul@paulhauner.com>"] authors = ["Paul Hauner <paul@paulhauner.com>"]
edition = "2021" edition = { workspace = true }
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
clap = "2.33.3" clap = { workspace = true }
hex = "0.4.2" hex = { workspace = true }
dirs = "3.0.1" dirs = { workspace = true }
eth2_network_config = { path = "../eth2_network_config" } eth2_network_config = { workspace = true }
ethereum_ssz = "0.5.0" ethereum_ssz = { workspace = true }
ethereum-types = "0.14.1" ethereum-types = { workspace = true }
serde = "1.0.116" serde = { workspace = true }
serde_json = "1.0.59" serde_json = { workspace = true }
serde_yaml = "0.8.13" serde_yaml = { workspace = true }
types = { path = "../../consensus/types"} types = { workspace = true }

View File

@ -2,10 +2,10 @@
name = "compare_fields" name = "compare_fields"
version = "0.2.0" version = "0.2.0"
authors = ["Paul Hauner <paul@paulhauner.com>"] authors = ["Paul Hauner <paul@paulhauner.com>"]
edition = "2021" edition = { workspace = true }
[dev-dependencies] [dev-dependencies]
compare_fields_derive = { path = "../compare_fields_derive" } compare_fields_derive = { workspace = true }
[package.metadata.cargo-udeps.ignore] [package.metadata.cargo-udeps.ignore]
development = ["compare_fields_derive"] # used in doc-tests development = ["compare_fields_derive"] # used in doc-tests

View File

@ -2,11 +2,11 @@
name = "compare_fields_derive" name = "compare_fields_derive"
version = "0.2.0" version = "0.2.0"
authors = ["Paul Hauner <paul@paulhauner.com>"] authors = ["Paul Hauner <paul@paulhauner.com>"]
edition = "2021" edition = { workspace = true }
[lib] [lib]
proc-macro = true proc-macro = true
[dependencies] [dependencies]
syn = "1.0.42" syn = { workspace = true }
quote = "1.0.7" quote = { workspace = true }

View File

@ -2,18 +2,18 @@
name = "deposit_contract" name = "deposit_contract"
version = "0.2.0" version = "0.2.0"
authors = ["Paul Hauner <paul@paulhauner.com>"] authors = ["Paul Hauner <paul@paulhauner.com>"]
edition = "2021" edition = { workspace = true }
build = "build.rs" build = "build.rs"
[build-dependencies] [build-dependencies]
reqwest = { version = "0.11.0", features = ["blocking", "json", "native-tls-vendored"] } reqwest = { workspace = true }
serde_json = "1.0.58" serde_json = { workspace = true }
sha2 = "0.10" sha2 = { workspace = true }
hex = "0.4.2" hex = { workspace = true }
[dependencies] [dependencies]
types = { path = "../../consensus/types"} types = { workspace = true }
ethereum_ssz = "0.5.0" ethereum_ssz = { workspace = true }
tree_hash = "0.5.2" tree_hash = { workspace = true }
ethabi = "16.0.0" ethabi = "16.0.0"

View File

@ -2,11 +2,11 @@
name = "directory" name = "directory"
version = "0.1.0" version = "0.1.0"
authors = ["pawan <pawandhananjay@gmail.com>"] authors = ["pawan <pawandhananjay@gmail.com>"]
edition = "2021" edition = { workspace = true }
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
clap = "2.33.3" clap = { workspace = true }
clap_utils = {path = "../clap_utils"} clap_utils = { workspace = true }
eth2_network_config = { path = "../eth2_network_config" } eth2_network_config = { workspace = true }

View File

@ -2,38 +2,38 @@
name = "eth2" name = "eth2"
version = "0.1.0" version = "0.1.0"
authors = ["Paul Hauner <paul@paulhauner.com>"] authors = ["Paul Hauner <paul@paulhauner.com>"]
edition = "2021" edition = { workspace = true }
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
serde = { version = "1.0.116", features = ["derive"] } serde = { workspace = true }
serde_json = "1.0.58" serde_json = { workspace = true }
ssz_types = "0.5.4" ssz_types = { workspace = true }
tree_hash = "0.5.2" tree_hash = { workspace = true }
types = { path = "../../consensus/types" } types = { workspace = true }
reqwest = { version = "0.11.0", features = ["json", "stream"] } reqwest = { workspace = true }
lighthouse_network = { path = "../../beacon_node/lighthouse_network" } lighthouse_network = { workspace = true }
proto_array = { path = "../../consensus/proto_array", optional = true } proto_array = { workspace = true }
ethereum_serde_utils = "0.5.0" ethereum_serde_utils = { workspace = true }
eth2_keystore = { path = "../../crypto/eth2_keystore" } eth2_keystore = { workspace = true }
libsecp256k1 = "0.7.0" libsecp256k1 = { workspace = true }
ring = "0.16.19" ring = { workspace = true }
bytes = "1.0.1" bytes = { workspace = true }
account_utils = { path = "../../common/account_utils" } account_utils = { workspace = true }
sensitive_url = { path = "../../common/sensitive_url" } sensitive_url = { workspace = true }
ethereum_ssz = "0.5.0" ethereum_ssz = { workspace = true }
ethereum_ssz_derive = "0.5.3" ethereum_ssz_derive = { workspace = true }
futures-util = "0.3.8" futures-util = "0.3.8"
futures = "0.3.8" futures = { workspace = true }
store = { path = "../../beacon_node/store", optional = true } store = { workspace = true }
slashing_protection = { path = "../../validator_client/slashing_protection", optional = true } slashing_protection = { workspace = true }
mediatype = "0.19.13" mediatype = "0.19.13"
mime = "0.3.16" mime = "0.3.16"
pretty_reqwest_error = { path = "../../common/pretty_reqwest_error" } pretty_reqwest_error = { workspace = true }
[dev-dependencies] [dev-dependencies]
tokio = { version = "1.14.0", features = ["full"] } tokio = { workspace = true }
[target.'cfg(target_os = "linux")'.dependencies] [target.'cfg(target_os = "linux")'.dependencies]
psutil = { version = "3.2.2", optional = true } psutil = { version = "3.2.2", optional = true }
@ -41,4 +41,4 @@ procfs = { version = "0.15.1", optional = true }
[features] [features]
default = ["lighthouse"] default = ["lighthouse"]
lighthouse = ["proto_array", "psutil", "procfs", "store", "slashing_protection"] lighthouse = ["psutil", "procfs"]

View File

@ -120,6 +120,7 @@ pub struct Timeouts {
pub get_beacon_blocks_ssz: Duration, pub get_beacon_blocks_ssz: Duration,
pub get_debug_beacon_states: Duration, pub get_debug_beacon_states: Duration,
pub get_deposit_snapshot: Duration, pub get_deposit_snapshot: Duration,
pub get_validator_block_ssz: Duration,
} }
impl Timeouts { impl Timeouts {
@ -135,6 +136,7 @@ impl Timeouts {
get_beacon_blocks_ssz: timeout, get_beacon_blocks_ssz: timeout,
get_debug_beacon_states: timeout, get_debug_beacon_states: timeout,
get_deposit_snapshot: timeout, get_deposit_snapshot: timeout,
get_validator_block_ssz: timeout,
} }
} }
} }
@ -1629,14 +1631,14 @@ impl BeaconNodeHttpClient {
.await .await
} }
/// `GET v2/validator/blocks/{slot}` /// returns `GET v2/validator/blocks/{slot}` URL path
pub async fn get_validator_blocks_modular<T: EthSpec, Payload: AbstractExecPayload<T>>( pub async fn get_validator_blocks_path<T: EthSpec, Payload: AbstractExecPayload<T>>(
&self, &self,
slot: Slot, slot: Slot,
randao_reveal: &SignatureBytes, randao_reveal: &SignatureBytes,
graffiti: Option<&Graffiti>, graffiti: Option<&Graffiti>,
skip_randao_verification: SkipRandaoVerification, skip_randao_verification: SkipRandaoVerification,
) -> Result<ForkVersionedResponse<BlockContents<T, Payload>>, Error> { ) -> Result<Url, Error> {
let mut path = self.eth_path(V2)?; let mut path = self.eth_path(V2)?;
path.path_segments_mut() path.path_segments_mut()
@ -1658,9 +1660,66 @@ impl BeaconNodeHttpClient {
.append_pair("skip_randao_verification", ""); .append_pair("skip_randao_verification", "");
} }
Ok(path)
}
/// `GET v2/validator/blocks/{slot}`
pub async fn get_validator_blocks_modular<T: EthSpec, Payload: AbstractExecPayload<T>>(
&self,
slot: Slot,
randao_reveal: &SignatureBytes,
graffiti: Option<&Graffiti>,
skip_randao_verification: SkipRandaoVerification,
) -> Result<ForkVersionedResponse<BlockContents<T, Payload>>, Error> {
let path = self
.get_validator_blocks_path::<T, Payload>(
slot,
randao_reveal,
graffiti,
skip_randao_verification,
)
.await?;
self.get(path).await self.get(path).await
} }
/// `GET v2/validator/blocks/{slot}` in ssz format
pub async fn get_validator_blocks_ssz<T: EthSpec, Payload: AbstractExecPayload<T>>(
&self,
slot: Slot,
randao_reveal: &SignatureBytes,
graffiti: Option<&Graffiti>,
) -> Result<Option<Vec<u8>>, Error> {
self.get_validator_blocks_modular_ssz::<T, Payload>(
slot,
randao_reveal,
graffiti,
SkipRandaoVerification::No,
)
.await
}
/// `GET v2/validator/blocks/{slot}` in ssz format
pub async fn get_validator_blocks_modular_ssz<T: EthSpec, Payload: AbstractExecPayload<T>>(
&self,
slot: Slot,
randao_reveal: &SignatureBytes,
graffiti: Option<&Graffiti>,
skip_randao_verification: SkipRandaoVerification,
) -> Result<Option<Vec<u8>>, Error> {
let path = self
.get_validator_blocks_path::<T, Payload>(
slot,
randao_reveal,
graffiti,
skip_randao_verification,
)
.await?;
self.get_bytes_opt_accept_header(path, Accept::Ssz, self.timeouts.get_validator_block_ssz)
.await
}
/// `GET v2/validator/blinded_blocks/{slot}` /// `GET v2/validator/blinded_blocks/{slot}`
pub async fn get_validator_blinded_blocks<T: EthSpec, Payload: AbstractExecPayload<T>>( pub async fn get_validator_blinded_blocks<T: EthSpec, Payload: AbstractExecPayload<T>>(
&self, &self,
@ -1677,17 +1736,14 @@ impl BeaconNodeHttpClient {
.await .await
} }
/// `GET v1/validator/blinded_blocks/{slot}` /// returns `GET v1/validator/blinded_blocks/{slot}` URL path
pub async fn get_validator_blinded_blocks_modular< pub async fn get_validator_blinded_blocks_path<T: EthSpec, Payload: AbstractExecPayload<T>>(
T: EthSpec,
Payload: AbstractExecPayload<T>,
>(
&self, &self,
slot: Slot, slot: Slot,
randao_reveal: &SignatureBytes, randao_reveal: &SignatureBytes,
graffiti: Option<&Graffiti>, graffiti: Option<&Graffiti>,
skip_randao_verification: SkipRandaoVerification, skip_randao_verification: SkipRandaoVerification,
) -> Result<ForkVersionedResponse<BlockContents<T, Payload>>, Error> { ) -> Result<Url, Error> {
let mut path = self.eth_path(V1)?; let mut path = self.eth_path(V1)?;
path.path_segments_mut() path.path_segments_mut()
@ -1709,9 +1765,71 @@ impl BeaconNodeHttpClient {
.append_key_only("skip_randao_verification"); .append_key_only("skip_randao_verification");
} }
Ok(path)
}
/// `GET v1/validator/blinded_blocks/{slot}`
pub async fn get_validator_blinded_blocks_modular<
T: EthSpec,
Payload: AbstractExecPayload<T>,
>(
&self,
slot: Slot,
randao_reveal: &SignatureBytes,
graffiti: Option<&Graffiti>,
skip_randao_verification: SkipRandaoVerification,
) -> Result<ForkVersionedResponse<BlockContents<T, Payload>>, Error> {
let path = self
.get_validator_blinded_blocks_path::<T, Payload>(
slot,
randao_reveal,
graffiti,
skip_randao_verification,
)
.await?;
self.get(path).await self.get(path).await
} }
/// `GET v2/validator/blinded_blocks/{slot}` in ssz format
pub async fn get_validator_blinded_blocks_ssz<T: EthSpec, Payload: AbstractExecPayload<T>>(
&self,
slot: Slot,
randao_reveal: &SignatureBytes,
graffiti: Option<&Graffiti>,
) -> Result<Option<Vec<u8>>, Error> {
self.get_validator_blinded_blocks_modular_ssz::<T, Payload>(
slot,
randao_reveal,
graffiti,
SkipRandaoVerification::No,
)
.await
}
pub async fn get_validator_blinded_blocks_modular_ssz<
T: EthSpec,
Payload: AbstractExecPayload<T>,
>(
&self,
slot: Slot,
randao_reveal: &SignatureBytes,
graffiti: Option<&Graffiti>,
skip_randao_verification: SkipRandaoVerification,
) -> Result<Option<Vec<u8>>, Error> {
let path = self
.get_validator_blinded_blocks_path::<T, Payload>(
slot,
randao_reveal,
graffiti,
skip_randao_verification,
)
.await?;
self.get_bytes_opt_accept_header(path, Accept::Ssz, self.timeouts.get_validator_block_ssz)
.await
}
/// `GET validator/attestation_data?slot,committee_index` /// `GET validator/attestation_data?slot,committee_index`
pub async fn get_validator_attestation_data( pub async fn get_validator_attestation_data(
&self, &self,

View File

@ -666,7 +666,7 @@ impl ValidatorClientHttpClient {
&self, &self,
pubkey: &PublicKeyBytes, pubkey: &PublicKeyBytes,
epoch: Option<Epoch>, epoch: Option<Epoch>,
) -> Result<SignedVoluntaryExit, Error> { ) -> Result<GenericResponse<SignedVoluntaryExit>, Error> {
let mut path = self.server.full.clone(); let mut path = self.server.full.clone();
path.path_segments_mut() path.path_segments_mut()

View File

@ -1468,9 +1468,10 @@ mod tests {
} }
/// A wrapper over a [`BeaconBlock`] or a [`BeaconBlockAndBlobSidecars`]. /// A wrapper over a [`BeaconBlock`] or a [`BeaconBlockAndBlobSidecars`].
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Encode, Serialize, Deserialize)]
#[serde(untagged)] #[serde(untagged)]
#[serde(bound = "T: EthSpec")] #[serde(bound = "T: EthSpec")]
#[ssz(enum_behaviour = "transparent")]
pub enum BlockContents<T: EthSpec, Payload: AbstractExecPayload<T>> { pub enum BlockContents<T: EthSpec, Payload: AbstractExecPayload<T>> {
BlockAndBlobSidecars(BeaconBlockAndBlobSidecars<T, Payload>), BlockAndBlobSidecars(BeaconBlockAndBlobSidecars<T, Payload>),
BlindedBlockAndBlobSidecars(BlindedBeaconBlockAndBlobSidecars<T, Payload>), BlindedBlockAndBlobSidecars(BlindedBeaconBlockAndBlobSidecars<T, Payload>),
@ -1483,6 +1484,58 @@ pub type BlockContentsTuple<T, Payload> = (
); );
impl<T: EthSpec, Payload: AbstractExecPayload<T>> BlockContents<T, Payload> { impl<T: EthSpec, Payload: AbstractExecPayload<T>> BlockContents<T, Payload> {
pub fn new(
block: BeaconBlock<T, Payload>,
blobs: Option<SidecarList<T, Payload::Sidecar>>,
) -> Self {
match (Payload::block_type(), blobs) {
(BlockType::Full, Some(blobs)) => {
Self::BlockAndBlobSidecars(BeaconBlockAndBlobSidecars {
block,
blob_sidecars: blobs,
})
}
(BlockType::Blinded, Some(blobs)) => {
Self::BlindedBlockAndBlobSidecars(BlindedBeaconBlockAndBlobSidecars {
blinded_block: block,
blinded_blob_sidecars: blobs,
})
}
(_, None) => Self::Block(block),
}
}
/// SSZ decode with fork variant determined by slot.
pub fn from_ssz_bytes(bytes: &[u8], spec: &ChainSpec) -> Result<Self, ssz::DecodeError> {
let slot_len = <Slot as Decode>::ssz_fixed_len();
let slot_bytes = bytes
.get(0..slot_len)
.ok_or(DecodeError::InvalidByteLength {
len: bytes.len(),
expected: slot_len,
})?;
let slot = Slot::from_ssz_bytes(slot_bytes)?;
let fork_at_slot = spec.fork_name_at_slot::<T>(slot);
match fork_at_slot {
ForkName::Base | ForkName::Altair | ForkName::Merge | ForkName::Capella => {
BeaconBlock::from_ssz_bytes(bytes, spec).map(|block| BlockContents::Block(block))
}
ForkName::Deneb => {
let mut builder = ssz::SszDecoderBuilder::new(bytes);
builder.register_anonymous_variable_length_item()?;
builder.register_type::<SidecarList<T, Payload::Sidecar>>()?;
let mut decoder = builder.build()?;
let block =
decoder.decode_next_with(|bytes| BeaconBlock::from_ssz_bytes(bytes, spec))?;
let blobs = decoder.decode_next()?;
Ok(BlockContents::new(block, Some(blobs)))
}
}
}
pub fn block(&self) -> &BeaconBlock<T, Payload> { pub fn block(&self) -> &BeaconBlock<T, Payload> {
match self { match self {
BlockContents::BlockAndBlobSidecars(block_and_sidecars) => &block_and_sidecars.block, BlockContents::BlockAndBlobSidecars(block_and_sidecars) => &block_and_sidecars.block,

View File

@ -2,8 +2,8 @@
name = "eth2_config" name = "eth2_config"
version = "0.2.0" version = "0.2.0"
authors = ["Paul Hauner <paul@paulhauner.com>"] authors = ["Paul Hauner <paul@paulhauner.com>"]
edition = "2021" edition = { workspace = true }
[dependencies] [dependencies]
types = { path = "../../consensus/types" } types = { workspace = true }
paste = "1.0.5" paste = { workspace = true }

View File

@ -29,7 +29,7 @@ const HOLESKY_GENESIS_STATE_SOURCE: GenesisStateSource = GenesisStateSource::Url
// more details. // more details.
"https://sigp-public-genesis-states.s3.ap-southeast-2.amazonaws.com/holesky/", "https://sigp-public-genesis-states.s3.ap-southeast-2.amazonaws.com/holesky/",
], ],
checksum: "0x76631cd0b9ddc5b2c766b496e23f16759ce1181446a4efb40e5540cd15b78a07", checksum: "0xd750639607c337bbb192b15c27f447732267bf72d1650180a0e44c2d93a80741",
genesis_validators_root: "0x9143aa7c615a7f7115e2b6aac319c03529df8242ae705fba9df39b79c59fa8b1", genesis_validators_root: "0x9143aa7c615a7f7115e2b6aac319c03529df8242ae705fba9df39b79c59fa8b1",
}; };

View File

@ -2,19 +2,19 @@
name = "eth2_interop_keypairs" name = "eth2_interop_keypairs"
version = "0.2.0" version = "0.2.0"
authors = ["Paul Hauner <paul@paulhauner.com>"] authors = ["Paul Hauner <paul@paulhauner.com>"]
edition = "2021" edition = { workspace = true }
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
lazy_static = "1.4.0" lazy_static = { workspace = true }
num-bigint = "0.4.2" num-bigint = "0.4.2"
ethereum_hashing = "1.0.0-beta.2" ethereum_hashing = { workspace = true }
hex = "0.4.2" hex = { workspace = true }
serde_yaml = "0.8.13" serde_yaml = { workspace = true }
serde = "1.0.116" serde = { workspace = true }
serde_derive = "1.0.116" serde_derive = "1.0.116"
bls = { path = "../../crypto/bls" } bls = { workspace = true }
[dev-dependencies] [dev-dependencies]
base64 = "0.13.0" base64 = "0.13.0"

View File

@ -2,29 +2,31 @@
name = "eth2_network_config" name = "eth2_network_config"
version = "0.2.0" version = "0.2.0"
authors = ["Paul Hauner <paul@paulhauner.com>"] authors = ["Paul Hauner <paul@paulhauner.com>"]
edition = "2021" edition = { workspace = true }
build = "build.rs" build = "build.rs"
[build-dependencies] [build-dependencies]
zip = "0.6" zip = { workspace = true }
eth2_config = { path = "../eth2_config" } eth2_config = { workspace = true }
[dev-dependencies] [dev-dependencies]
tempfile = "3.1.0" tempfile = { workspace = true }
tokio = { workspace = true }
[dependencies] [dependencies]
serde_yaml = "0.8.13" serde_yaml = { workspace = true }
serde_json = "1.0.58" serde_json = { workspace = true }
types = { path = "../../consensus/types" } types = { workspace = true }
kzg = { path = "../../crypto/kzg" } kzg = { workspace = true }
ethereum_ssz = "0.5.0" ethereum_ssz = { workspace = true }
eth2_config = { path = "../eth2_config" } eth2_config = { workspace = true }
discv5 = "0.3.1" discv5 = { workspace = true }
reqwest = { version = "0.11.0", features = ["blocking"] } reqwest = { workspace = true }
pretty_reqwest_error = { path = "../pretty_reqwest_error" } pretty_reqwest_error = { workspace = true }
sha2 = "0.10" sha2 = { workspace = true }
url = "2.2.2" url = { workspace = true }
sensitive_url = { path = "../sensitive_url" } sensitive_url = { workspace = true }
slog = "2.5.2" slog = { workspace = true }
logging = { path = "../logging" } logging = { workspace = true }
bytes = { workspace = true }

View File

@ -1,8 +1,9 @@
# EF # EF
- enr:-Iq4QJk4WqRkjsX5c2CXtOra6HnxN-BMXnWhmhEQO9Bn9iABTJGdjUOurM7Btj1ouKaFkvTRoju5vz2GPmVON2dffQKGAX53x8JigmlkgnY0gmlwhLKAlv6Jc2VjcDI1NmsxoQK6S-Cii_KmfFdUJL2TANL3ksaKUnNXvTCv1tLwXs0QgIN1ZHCCIyk - enr:-Ku4QFo-9q73SspYI8cac_4kTX7yF800VXqJW4Lj3HkIkb5CMqFLxciNHePmMt4XdJzHvhrCC5ADI4D_GkAsxGJRLnQBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpAhnTT-AQFwAP__________gmlkgnY0gmlwhLKAiOmJc2VjcDI1NmsxoQORcM6e19T1T9gi7jxEZjk_sjVLGFscUNqAY9obgZaxbIN1ZHCCIyk
- enr:-KG4QF6d6vMSboSujAXTI4vYqArccm0eIlXfcxf2Lx_VE1q6IkQo_2D5LAO3ZSBVUs0w5rrVDmABJZuMzISe_pZundADhGV0aDKQqX6DZjABcAAAAQAAAAAAAIJpZIJ2NIJpcISygIjpiXNlY3AyNTZrMaEDF3aSa7QSCvdqLpANNd8GML4PLEZVg45fKQwMWhDZjd2DdGNwgiMog3VkcIIjKA - enr:-Ku4QPG7F72mbKx3gEQEx07wpYYusGDh-ni6SNkLvOS-hhN-BxIggN7tKlmalb0L5JPoAfqD-akTZ-gX06hFeBEz4WoBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpAhnTT-AQFwAP__________gmlkgnY0gmlwhJK-DYCJc2VjcDI1NmsxoQKLVXFOhp2uX6jeT0DvvDpPcU8FWMjQdR4wMuORMhpX24N1ZHCCIyk
- enr:-Ly4QJLXSSAj3ggPBIcodvBU6IyfpU_yW7E9J-5syoJorBuvcYj_Fokcjr303bQoTdWXADf8po0ssh75Mr5wVGzZZsMBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpCpfoNmMAFwAAABAAAAAAAAgmlkgnY0gmlwhJK-DYCJc2VjcDI1NmsxoQJrIlXIQDvQ6t9yDySqJYDXgZgLXzTvq8W7OI51jfmxJohzeW5jbmV0cwCDdGNwgiMog3VkcIIjKA - enr:-LK4QPxe-mDiSOtEB_Y82ozvxn9aQM07Ui8A-vQHNgYGMMthfsfOabaaTHhhJHFCBQQVRjBww_A5bM1rf8MlkJU_l68Eh2F0dG5ldHOIAADAAAAAAACEZXRoMpBpt9l0BAFwAAABAAAAAAAAgmlkgnY0gmlwhLKAiOmJc2VjcDI1NmsxoQJu6T9pclPObAzEVQ53DpVQqjadmVxdTLL-J3h9NFoCeIN0Y3CCIyiDdWRwgiMo
- enr:-Ly4QGbOw4xNel5EhmDsJJ-QhC9XycWtsetnWoZ0uRy381GHdHsNHJiCwDTOkb3S1Ade0SFQkWJX_pgb3g8Jfh93rvMBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpBpt9l0BAFwAAABAAAAAAAAgmlkgnY0gmlwhJK-DYCJc2VjcDI1NmsxoQOxKv9sv3zKF8GDewgFGGHKP5HCZZpPpTrwl9eXKAWGxIhzeW5jbmV0cwCDdGNwgiMog3VkcIIjKA
# Teku # Teku
- enr:-LK4QMlzEff6d-M0A1pSFG5lJ2c56i_I-ZftdojZbW3ehkGNM4pkQuHQqzVvF1BG9aDjIakjnmO23mCBFFZ2w5zOsugEh2F0dG5ldHOIAAAAAAYAAACEZXRoMpCpfoNmMAFwAAABAAAAAAAAgmlkgnY0gmlwhKyuI_mJc2VjcDI1NmsxoQIH1kQRCZW-4AIVyAeXj5o49m_IqNFKRHp6tSpfXMUrSYN0Y3CCIyiDdWRwgiMo - enr:-LS4QG0uV4qvcpJ-HFDJRGBmnlD3TJo7yc4jwK8iP7iKaTlfQ5kZvIDspLMJhk7j9KapuL9yyHaZmwTEZqr10k9XumyCEcmHYXR0bmV0c4gAAAAABgAAAIRldGgykGm32XQEAXAAAAEAAAAAAACCaWSCdjSCaXCErK4j-YlzZWNwMjU2azGhAgfWRBEJlb7gAhXIB5ePmjj2b8io0UpEenq1Kl9cxStJg3RjcIIjKIN1ZHCCIyg
# Sigma Prime # Sigma Prime
- enr:-Le4QI88slOwzz66Ksq8Vnz324DPb1BzSiY-WYPvnoJIl-lceW9bmSJnwDzgNbCjp5wsBigg76x4tValvGgQPxxSjrMBhGV0aDKQqX6DZjABcAAAAQAAAAAAAIJpZIJ2NIJpcIQ5gR6Wg2lwNpAgAUHQBwEQAAAAAAAAADR-iXNlY3AyNTZrMaEDPMSNdcL92uNIyCsS177Z6KTXlbZakQqxv3aQcWawNXeDdWRwgiMohHVkcDaCI4I - enr:-Le4QLoE1wFHSlGcm48a9ZESb_MRLqPPu6G0vHqu4MaUcQNDHS69tsy-zkN0K6pglyzX8m24mkb-LtBcbjAYdP1uxm4BhGV0aDKQabfZdAQBcAAAAQAAAAAAAIJpZIJ2NIJpcIQ5gR6Wg2lwNpAgAUHQBwEQAAAAAAAAADR-iXNlY3AyNTZrMaEDPMSNdcL92uNIyCsS177Z6KTXlbZakQqxv3aQcWawNXeDdWRwgiMohHVkcDaCI4I

View File

@ -6,9 +6,9 @@ CONFIG_NAME: holesky
# --------------------------------------------------------------- # ---------------------------------------------------------------
# `2**14` (= 16,384) # `2**14` (= 16,384)
MIN_GENESIS_ACTIVE_VALIDATOR_COUNT: 16384 MIN_GENESIS_ACTIVE_VALIDATOR_COUNT: 16384
# Sep-15-2023 13:55:00 +UTC # Sep-28-2023 11:55:00 +UTC
MIN_GENESIS_TIME: 1694786100 MIN_GENESIS_TIME: 1695902100
GENESIS_FORK_VERSION: 0x00017000 GENESIS_FORK_VERSION: 0x01017000
# Genesis delay 5 mins # Genesis delay 5 mins
GENESIS_DELAY: 300 GENESIS_DELAY: 300
@ -20,23 +20,19 @@ GENESIS_DELAY: 300
# - Temporarily set to max uint64 value: 2**64 - 1 # - Temporarily set to max uint64 value: 2**64 - 1
# Altair # Altair
ALTAIR_FORK_VERSION: 0x10017000 ALTAIR_FORK_VERSION: 0x02017000
ALTAIR_FORK_EPOCH: 0 ALTAIR_FORK_EPOCH: 0
# Merge # Merge
BELLATRIX_FORK_VERSION: 0x20017000 BELLATRIX_FORK_VERSION: 0x03017000
BELLATRIX_FORK_EPOCH: 0 BELLATRIX_FORK_EPOCH: 0
TERMINAL_TOTAL_DIFFICULTY: 0 TERMINAL_TOTAL_DIFFICULTY: 0
TERMINAL_BLOCK_HASH: 0x0000000000000000000000000000000000000000000000000000000000000000 TERMINAL_BLOCK_HASH: 0x0000000000000000000000000000000000000000000000000000000000000000
TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH: 18446744073709551615 TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH: 18446744073709551615
# Capella # Capella
CAPELLA_FORK_VERSION: 0x30017000 CAPELLA_FORK_VERSION: 0x04017000
CAPELLA_FORK_EPOCH: 256 CAPELLA_FORK_EPOCH: 256
# DENEB
DENEB_FORK_VERSION: 0x40017000
DENEB_FORK_EPOCH: 18446744073709551615
# Time parameters # Time parameters
# --------------------------------------------------------------- # ---------------------------------------------------------------
# 12 seconds # 12 seconds

View File

@ -10,11 +10,13 @@
- 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:-KG4QOtcP9X1FbIMOe17QNMKqDxCpm14jcX5tiOE4_TyMrFqbmhPZHK_ZPG2Gxb1GE2xdtodOfx9-cgvNtxnRyHEmC0ghGV0aDKQ9aX9QgAAAAD__________4JpZIJ2NIJpcIQDE8KdiXNlY3AyNTZrMaEDhpehBDbZjM_L9ek699Y7vhUJ-eAdMyQW_Fil522Y0fODdGNwgiMog3VkcIIjKA
- enr:-KG4QDyytgmE4f7AnvW-ZaUOIi9i79qX4JwjRAiXBZCU65wOfBu-3Nb5I7b_Rmg3KCOcZM_C3y5pg7EBU5XGrcLTduQEhGV0aDKQ9aX9QgAAAAD__________4JpZIJ2NIJpcIQ2_DUbiXNlY3AyNTZrMaEDKnz_-ps3UUOfHWVYaskI5kWYO_vtYMGYCQRAR3gHDouDdGNwgiMog3VkcIIjKA - enr:-KG4QL-eqFoHy0cI31THvtZjpYUu_Jdw_MO7skQRJxY1g5HTN1A0epPCU6vi0gLGUgrzpU-ygeMSS8ewVxDpKfYmxMMGhGV0aDKQtTA_KgAAAAD__________4JpZIJ2NIJpcIQ2_DUbiXNlY3AyNTZrMaED8GJ2vzUqgL6-KD1xalo1CsmY4X1HaDnyl6Y_WayCo9GDdGNwgiMog3VkcIIjKA
- enr:-KG4QMOEswP62yzDjSwWS4YEjtTZ5PO6r65CPqYBkgTTkrpaedQ8uEUo1uMALtJIvb2w_WWEVmg5yt1UAuK1ftxUU7QDhGV0aDKQu6TalgMAAAD__________4JpZIJ2NIJpcIQEnfA2iXNlY3AyNTZrMaEDfol8oLr6XJ7FsdAYE7lpJhKMls4G_v6qQOGKJUWGb_uDdGNwgiMog3VkcIIjKA
- enr:-KG4QF4B5WrlFcRhUU6dZETwY5ZzAXnA0vGC__L1Kdw602nDZwXSTs5RFXFIFUnbQJmhNGVU6OIX7KVrCSTODsz1tK4DhGV0aDKQu6TalgMAAAD__________4JpZIJ2NIJpcIQExNYEiXNlY3AyNTZrMaECQmM9vp7KhaXhI-nqL_R0ovULLCFSFTa9CPPSdb1zPX6DdGNwgiMog3VkcIIjKA
# Prysm team (Prysmatic Labs) # Prysm team (Prysmatic Labs)
- enr:-Ku4QImhMc1z8yCiNJ1TyUxdcfNucje3BGwEHzodEZUan8PherEo4sF7pPHPSIB1NNuSg5fZy7qFsjmUKs2ea1Whi0EBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpD1pf1CAAAAAP__________gmlkgnY0gmlwhBLf22SJc2VjcDI1NmsxoQOVphkDqal4QzPMksc5wnpuC3gvSC8AfbFOnZY_On34wIN1ZHCCIyg - enr:-Ku4QImhMc1z8yCiNJ1TyUxdcfNucje3BGwEHzodEZUan8PherEo4sF7pPHPSIB1NNuSg5fZy7qFsjmUKs2ea1Whi0EBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpD1pf1CAAAAAP__________gmlkgnY0gmlwhBLf22SJc2VjcDI1NmsxoQOVphkDqal4QzPMksc5wnpuC3gvSC8AfbFOnZY_On34wIN1ZHCCIyg
- enr:-Ku4QP2xDnEtUXIjzJ_DhlCRN9SN99RYQPJL92TMlSv7U5C1YnYLjwOQHgZIUXw6c-BvRg2Yc2QsZxxoS_pPRVe0yK8Bh2F0dG5ldHOIAAAAAAAAAACEZXRoMpD1pf1CAAAAAP__________gmlkgnY0gmlwhBLf22SJc2VjcDI1NmsxoQMeFF5GrS7UZpAH2Ly84aLK-TyvH-dRo0JM1i8yygH50YN1ZHCCJxA - enr:-Ku4QP2xDnEtUXIjzJ_DhlCRN9SN99RYQPJL92TMlSv7U5C1YnYLjwOQHgZIUXw6c-BvRg2Yc2QsZxxoS_pPRVe0yK8Bh2F0dG5ldHOIAAAAAAAAAACEZXRoMpD1pf1CAAAAAP__________gmlkgnY0gmlwhBLf22SJc2VjcDI1NmsxoQMeFF5GrS7UZpAH2Ly84aLK-TyvH-dRo0JM1i8yygH50YN1ZHCCJxA
- enr:-Ku4QPp9z1W4tAO8Ber_NQierYaOStqhDqQdOPY3bB3jDgkjcbk6YrEnVYIiCBbTxuar3CzS528d2iE7TdJsrL-dEKoBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpD1pf1CAAAAAP__________gmlkgnY0gmlwhBLf22SJc2VjcDI1NmsxoQMw5fqqkw2hHC4F5HZZDPsNmPdB1Gi8JPQK7pRc9XHh-oN1ZHCCKvg - enr:-Ku4QPp9z1W4tAO8Ber_NQierYaOStqhDqQdOPY3bB3jDgkjcbk6YrEnVYIiCBbTxuar3CzS528d2iE7TdJsrL-dEKoBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpD1pf1CAAAAAP__________gmlkgnY0gmlwhBLf22SJc2VjcDI1NmsxoQMw5fqqkw2hHC4F5HZZDPsNmPdB1Gi8JPQK7pRc9XHh-oN1ZHCCKvg
# Nimbus team # Nimbus team
- enr:-LK4QA8FfhaAjlb_BXsXxSfiysR7R52Nhi9JBt4F8SPssu8hdE1BXQQEtVDC3qStCW60LSO7hEsVHv5zm8_6Vnjhcn0Bh2F0dG5ldHOIAAAAAAAAAACEZXRoMpC1MD8qAAAAAP__________gmlkgnY0gmlwhAN4aBKJc2VjcDI1NmsxoQJerDhsJ-KxZ8sHySMOCmTO6sHM3iCFQ6VMvLTe948MyYN0Y3CCI4yDdWRwgiOM - enr:-LK4QA8FfhaAjlb_BXsXxSfiysR7R52Nhi9JBt4F8SPssu8hdE1BXQQEtVDC3qStCW60LSO7hEsVHv5zm8_6Vnjhcn0Bh2F0dG5ldHOIAAAAAAAAAACEZXRoMpC1MD8qAAAAAP__________gmlkgnY0gmlwhAN4aBKJc2VjcDI1NmsxoQJerDhsJ-KxZ8sHySMOCmTO6sHM3iCFQ6VMvLTe948MyYN0Y3CCI4yDdWRwgiOM
- enr:-LK4QKWrXTpV9T78hNG6s8AM6IO4XH9kFT91uZtFg1GcsJ6dKovDOr1jtAAFPnS2lvNltkOGA9k29BUN7lFh_sjuc9QBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpC1MD8qAAAAAP__________gmlkgnY0gmlwhANAdd-Jc2VjcDI1NmsxoQLQa6ai7y9PMN5hpLe5HmiJSlYzMuzP7ZhwRiwHvqNXdoN0Y3CCI4yDdWRwgiOM - enr:-LK4QKWrXTpV9T78hNG6s8AM6IO4XH9kFT91uZtFg1GcsJ6dKovDOr1jtAAFPnS2lvNltkOGA9k29BUN7lFh_sjuc9QBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpC1MD8qAAAAAP__________gmlkgnY0gmlwhANAdd-Jc2VjcDI1NmsxoQLQa6ai7y9PMN5hpLe5HmiJSlYzMuzP7ZhwRiwHvqNXdoN0Y3CCI4yDdWRwgiOM

View File

@ -11,11 +11,12 @@
//! To add a new built-in testnet, add it to the `define_hardcoded_nets` invocation in the `eth2_config` //! To add a new built-in testnet, add it to the `define_hardcoded_nets` invocation in the `eth2_config`
//! crate. //! crate.
use bytes::Bytes;
use discv5::enr::{CombinedKey, Enr}; use discv5::enr::{CombinedKey, Enr};
use eth2_config::{instantiate_hardcoded_nets, HardcodedNet}; use eth2_config::{instantiate_hardcoded_nets, HardcodedNet};
use kzg::{KzgPreset, KzgPresetId, TrustedSetup}; use kzg::{KzgPreset, KzgPresetId, TrustedSetup};
use pretty_reqwest_error::PrettyReqwestError; use pretty_reqwest_error::PrettyReqwestError;
use reqwest::blocking::Client; use reqwest::{Client, Error};
use sensitive_url::SensitiveUrl; use sensitive_url::SensitiveUrl;
use sha2::{Digest, Sha256}; use sha2::{Digest, Sha256};
use slog::{info, warn, Logger}; use slog::{info, warn, Logger};
@ -168,14 +169,8 @@ impl Eth2NetworkConfig {
self.genesis_state_source != GenesisStateSource::Unknown self.genesis_state_source != GenesisStateSource::Unknown
} }
/// The `genesis_validators_root` of the genesis state. May download the /// The `genesis_validators_root` of the genesis state.
/// genesis state if the value is not already available. pub fn genesis_validators_root<E: EthSpec>(&self) -> Result<Option<Hash256>, String> {
pub fn genesis_validators_root<E: EthSpec>(
&self,
genesis_state_url: Option<&str>,
timeout: Duration,
log: &Logger,
) -> Result<Option<Hash256>, String> {
if let GenesisStateSource::Url { if let GenesisStateSource::Url {
genesis_validators_root, genesis_validators_root,
.. ..
@ -190,10 +185,8 @@ impl Eth2NetworkConfig {
) )
}) })
} else { } else {
self.genesis_state::<E>(genesis_state_url, timeout, log)? self.get_genesis_state_from_bytes::<E>()
.map(|state| state.genesis_validators_root()) .map(|state| Some(state.genesis_validators_root()))
.map(Result::Ok)
.transpose()
} }
} }
@ -211,7 +204,7 @@ impl Eth2NetworkConfig {
/// ///
/// If the genesis state is configured to be downloaded from a URL, then the /// If the genesis state is configured to be downloaded from a URL, then the
/// `genesis_state_url` will override the built-in list of download URLs. /// `genesis_state_url` will override the built-in list of download URLs.
pub fn genesis_state<E: EthSpec>( pub async fn genesis_state<E: EthSpec>(
&self, &self,
genesis_state_url: Option<&str>, genesis_state_url: Option<&str>,
timeout: Duration, timeout: Duration,
@ -221,15 +214,7 @@ impl Eth2NetworkConfig {
match &self.genesis_state_source { match &self.genesis_state_source {
GenesisStateSource::Unknown => Ok(None), GenesisStateSource::Unknown => Ok(None),
GenesisStateSource::IncludedBytes => { GenesisStateSource::IncludedBytes => {
let state = self let state = self.get_genesis_state_from_bytes()?;
.genesis_state_bytes
.as_ref()
.map(|bytes| {
BeaconState::from_ssz_bytes(bytes.as_ref(), &spec).map_err(|e| {
format!("Built-in genesis state SSZ bytes are invalid: {:?}", e)
})
})
.ok_or("Genesis state bytes missing from Eth2NetworkConfig")??;
Ok(Some(state)) Ok(Some(state))
} }
GenesisStateSource::Url { GenesisStateSource::Url {
@ -241,9 +226,9 @@ impl Eth2NetworkConfig {
format!("Unable to parse genesis state bytes checksum: {:?}", e) format!("Unable to parse genesis state bytes checksum: {:?}", e)
})?; })?;
let bytes = if let Some(specified_url) = genesis_state_url { let bytes = if let Some(specified_url) = genesis_state_url {
download_genesis_state(&[specified_url], timeout, checksum, log) download_genesis_state(&[specified_url], timeout, checksum, log).await
} else { } else {
download_genesis_state(built_in_urls, timeout, checksum, log) download_genesis_state(built_in_urls, timeout, checksum, log).await
}?; }?;
let state = BeaconState::from_ssz_bytes(bytes.as_ref(), &spec).map_err(|e| { let state = BeaconState::from_ssz_bytes(bytes.as_ref(), &spec).map_err(|e| {
format!("Downloaded genesis state SSZ bytes are invalid: {:?}", e) format!("Downloaded genesis state SSZ bytes are invalid: {:?}", e)
@ -269,6 +254,17 @@ impl Eth2NetworkConfig {
} }
} }
fn get_genesis_state_from_bytes<E: EthSpec>(&self) -> Result<BeaconState<E>, String> {
let spec = self.chain_spec::<E>()?;
self.genesis_state_bytes
.as_ref()
.map(|bytes| {
BeaconState::from_ssz_bytes(bytes.as_ref(), &spec)
.map_err(|e| format!("Built-in genesis state SSZ bytes are invalid: {:?}", e))
})
.ok_or("Genesis state bytes missing from Eth2NetworkConfig")?
}
/// Write the files to the directory. /// Write the files to the directory.
/// ///
/// Overwrites files if specified to do so. /// Overwrites files if specified to do so.
@ -396,7 +392,7 @@ impl Eth2NetworkConfig {
/// Try to download a genesis state from each of the `urls` in the order they /// Try to download a genesis state from each of the `urls` in the order they
/// are defined. Return `Ok` if any url returns a response that matches the /// are defined. Return `Ok` if any url returns a response that matches the
/// given `checksum`. /// given `checksum`.
fn download_genesis_state( async fn download_genesis_state(
urls: &[&str], urls: &[&str],
timeout: Duration, timeout: Duration,
checksum: Hash256, checksum: Hash256,
@ -428,12 +424,7 @@ fn download_genesis_state(
); );
let client = Client::new(); let client = Client::new();
let response = client let response = get_state_bytes(timeout, url, client).await;
.get(url)
.header("Accept", "application/octet-stream")
.timeout(timeout)
.send()
.and_then(|r| r.error_for_status().and_then(|r| r.bytes()));
match response { match response {
Ok(bytes) => { Ok(bytes) => {
@ -463,6 +454,18 @@ fn download_genesis_state(
)) ))
} }
async fn get_state_bytes(timeout: Duration, url: Url, client: Client) -> Result<Bytes, Error> {
client
.get(url)
.header("Accept", "application/octet-stream")
.timeout(timeout)
.send()
.await?
.error_for_status()?
.bytes()
.await
}
/// Parses the `url` and joins the necessary state download path. /// Parses the `url` and joins the necessary state download path.
fn parse_state_download_url(url: &str) -> Result<Url, String> { fn parse_state_download_url(url: &str) -> Result<Url, String> {
Url::parse(url) Url::parse(url)
@ -507,11 +510,12 @@ mod tests {
assert_eq!(spec, config.chain_spec::<GnosisEthSpec>().unwrap()); assert_eq!(spec, config.chain_spec::<GnosisEthSpec>().unwrap());
} }
#[test] #[tokio::test]
fn mainnet_genesis_state() { async fn mainnet_genesis_state() {
let config = Eth2NetworkConfig::from_hardcoded_net(&MAINNET).unwrap(); let config = Eth2NetworkConfig::from_hardcoded_net(&MAINNET).unwrap();
config config
.genesis_state::<E>(None, Duration::from_secs(1), &logging::test_logger()) .genesis_state::<E>(None, Duration::from_secs(1), &logging::test_logger())
.await
.expect("beacon state can decode"); .expect("beacon state can decode");
} }

View File

@ -2,13 +2,13 @@
name = "eth2_wallet_manager" name = "eth2_wallet_manager"
version = "0.1.0" version = "0.1.0"
authors = ["Paul Hauner <paul@paulhauner.com>"] authors = ["Paul Hauner <paul@paulhauner.com>"]
edition = "2021" edition = { workspace = true }
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
eth2_wallet = { path = "../../crypto/eth2_wallet" } eth2_wallet = { workspace = true }
lockfile = { path = "../lockfile" } lockfile = { workspace = true }
[dev-dependencies] [dev-dependencies]
tempfile = "3.1.0" tempfile = { workspace = true }

View File

@ -2,7 +2,7 @@
name = "filesystem" name = "filesystem"
version = "0.1.0" version = "0.1.0"
authors = ["Mark Mackey <mark@sigmaprime.io>"] authors = ["Mark Mackey <mark@sigmaprime.io>"]
edition = "2021" edition = { workspace = true }
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@ -2,10 +2,10 @@
name = "lighthouse_metrics" name = "lighthouse_metrics"
version = "0.2.0" version = "0.2.0"
authors = ["Paul Hauner <paul@paulhauner.com>"] authors = ["Paul Hauner <paul@paulhauner.com>"]
edition = "2021" edition = { workspace = true }
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
lazy_static = "1.4.0" lazy_static = { workspace = true }
prometheus = "0.13.0" prometheus = "0.13.0"

View File

@ -2,7 +2,7 @@
name = "lighthouse_version" name = "lighthouse_version"
version = "0.1.0" version = "0.1.0"
authors = ["Paul Hauner <paul@paulhauner.com>"] authors = ["Paul Hauner <paul@paulhauner.com>"]
edition = "2021" edition = { workspace = true }
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@ -11,4 +11,4 @@ git-version = "0.3.4"
target_info = "0.1.0" target_info = "0.1.0"
[dev-dependencies] [dev-dependencies]
regex = "1.5.5" regex = { workspace = true }

View File

@ -17,8 +17,8 @@ pub const VERSION: &str = git_version!(
// NOTE: using --match instead of --exclude for compatibility with old Git // NOTE: using --match instead of --exclude for compatibility with old Git
"--match=thiswillnevermatchlol" "--match=thiswillnevermatchlol"
], ],
prefix = "Lighthouse/v4.4.1-", prefix = "Lighthouse/v4.5.0-",
fallback = "Lighthouse/v4.4.1" fallback = "Lighthouse/v4.5.0"
); );
/// Returns `VERSION`, but with platform information appended to the end. /// Returns `VERSION`, but with platform information appended to the end.

View File

@ -2,10 +2,10 @@
name = "lockfile" name = "lockfile"
version = "0.1.0" version = "0.1.0"
authors = ["Michael Sproul <michael@sigmaprime.io>"] authors = ["Michael Sproul <michael@sigmaprime.io>"]
edition = "2021" edition = { workspace = true }
[dependencies] [dependencies]
fs2 = "0.4.3" fs2 = { workspace = true }
[dev-dependencies] [dev-dependencies]
tempfile = "3.1.0" tempfile = { workspace = true }

View File

@ -2,21 +2,21 @@
name = "logging" name = "logging"
version = "0.2.0" version = "0.2.0"
authors = ["blacktemplar <blacktemplar@a1.net>"] authors = ["blacktemplar <blacktemplar@a1.net>"]
edition = "2021" edition = { workspace = true }
[features] [features]
test_logger = [] # Print log output to stderr when running tests instead of dropping it test_logger = [] # Print log output to stderr when running tests instead of dropping it
[dependencies] [dependencies]
slog = "2.5.2" slog = { workspace = true }
slog-term = "2.6.0" slog-term = { workspace = true }
tokio = { version = "1.26.0", features = ["sync"] } tokio = { workspace = true }
lighthouse_metrics = { path = "../lighthouse_metrics" } lighthouse_metrics = { workspace = true }
lazy_static = "1.4.0" lazy_static = { workspace = true }
sloggers = { version = "2.1.1", features = ["json"] } sloggers = { workspace = true }
slog-async = "2.7.0" slog-async = { workspace = true }
take_mut = "0.2.2" take_mut = "0.2.2"
parking_lot = "0.12.1" parking_lot = { workspace = true }
serde = "1.0.153" serde = { workspace = true }
serde_json = "1.0.94" serde_json = { workspace = true }
chrono = { version = "0.4", default-features = false, features = ["clock", "std"] } chrono = { version = "0.4", default-features = false, features = ["clock", "std"] }

View File

@ -2,7 +2,7 @@
name = "lru_cache" name = "lru_cache"
version = "0.1.0" version = "0.1.0"
authors = ["Sigma Prime <contact@sigmaprime.io>"] authors = ["Sigma Prime <contact@sigmaprime.io>"]
edition = "2021" edition = { workspace = true }
[dependencies] [dependencies]
fnv = "1.0.7" fnv = { workspace = true }

View File

@ -2,13 +2,13 @@
name = "malloc_utils" name = "malloc_utils"
version = "0.1.0" version = "0.1.0"
authors = ["Paul Hauner <paul@paulhauner.com>"] authors = ["Paul Hauner <paul@paulhauner.com>"]
edition = "2021" edition = { workspace = true }
[dependencies] [dependencies]
lighthouse_metrics = { path = "../lighthouse_metrics" } lighthouse_metrics = { workspace = true }
lazy_static = "1.4.0" lazy_static = { workspace = true }
libc = "0.2.79" libc = "0.2.79"
parking_lot = "0.12.0" parking_lot = { workspace = true }
jemalloc-ctl = { version = "0.5.0", optional = true } jemalloc-ctl = { version = "0.5.0", optional = true }
# Jemalloc's background_threads feature requires Linux (pthreads). # Jemalloc's background_threads feature requires Linux (pthreads).

View File

@ -2,22 +2,22 @@
name = "monitoring_api" name = "monitoring_api"
version = "0.1.0" version = "0.1.0"
authors = ["pawan <pawandhananjay@gmail.com>"] authors = ["pawan <pawandhananjay@gmail.com>"]
edition = "2021" edition = { workspace = true }
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
reqwest = { version = "0.11.0", features = ["json","stream"] } reqwest = { workspace = true }
task_executor = { path = "../task_executor" } task_executor = { workspace = true }
tokio = "1.14.0" tokio = { workspace = true }
eth2 = {path = "../eth2"} eth2 = { workspace = true }
serde_json = "1.0.58" serde_json = { workspace = true }
serde = "1.0.116"
serde_derive = "1.0.116" serde_derive = "1.0.116"
lighthouse_version = { path = "../lighthouse_version"} serde = { workspace = true }
lighthouse_metrics = { path = "../lighthouse_metrics" } lighthouse_version = { workspace = true }
slog = "2.5.2" lighthouse_metrics = { workspace = true }
store = { path = "../../beacon_node/store" } slog = { workspace = true }
lazy_static = "1.4.0" store = { workspace = true }
regex = "1.5.5" lazy_static = { workspace = true }
sensitive_url = { path = "../sensitive_url" } regex = { workspace = true }
sensitive_url = { workspace = true }

View File

@ -1,9 +1,9 @@
[package] [package]
name = "oneshot_broadcast" name = "oneshot_broadcast"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = { workspace = true }
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
parking_lot = "0.12.0" parking_lot = { workspace = true }

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