Merge unstable 20230925 into deneb-free-blobs.

This commit is contained in:
Jimmy Chen 2023-09-26 10:32:18 +10:00
commit c0b6b92f27
No known key found for this signature in database
GPG Key ID: 7AAEE02659DCF690
164 changed files with 3844 additions and 3057 deletions

View File

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

View File

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

View File

@ -58,7 +58,7 @@ jobs:
release-tests-ubuntu:
name: release-tests-ubuntu
# 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
steps:
- uses: actions/checkout@v3
@ -73,7 +73,7 @@ jobs:
run: make test-release
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
steps:
- uses: actions/checkout@v3
@ -98,7 +98,7 @@ jobs:
beacon-chain-tests:
name: beacon-chain-tests
# 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
steps:
- uses: actions/checkout@v3
@ -140,7 +140,7 @@ jobs:
debug-tests-ubuntu:
name: debug-tests-ubuntu
# 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
steps:
- uses: actions/checkout@v3
@ -166,7 +166,7 @@ jobs:
ef-tests-ubuntu:
name: ef-tests-ubuntu
# 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
steps:
- 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"
[patch]
[patch.crates-io]
# TODO: remove when 0.3.6 get's released.
warp = { git = "https://github.com/seanmonstar/warp.git", rev="149913fe" }
[workspace.package]
edition = "2021"
[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]
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
COPY . lighthouse
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)
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.
vendor:

View File

@ -5,31 +5,31 @@ authors = [
"Paul Hauner <paul@paulhauner.com>",
"Luke Anderson <luke@sigmaprime.io>",
]
edition = "2021"
edition = { workspace = true }
[dependencies]
bls = { path = "../crypto/bls" }
clap = "2.33.3"
types = { path = "../consensus/types" }
environment = { path = "../lighthouse/environment" }
eth2_network_config = { path = "../common/eth2_network_config" }
clap_utils = { path = "../common/clap_utils" }
directory = { path = "../common/directory" }
eth2_wallet = { path = "../crypto/eth2_wallet" }
bls = { workspace = true }
clap = { workspace = true }
types = { workspace = true }
environment = { workspace = true }
eth2_network_config = { workspace = true }
clap_utils = { workspace = true }
directory = { workspace = true }
eth2_wallet = { workspace = true }
eth2_wallet_manager = { path = "../common/eth2_wallet_manager" }
validator_dir = { path = "../common/validator_dir" }
tokio = { version = "1.14.0", features = ["full"] }
eth2_keystore = { path = "../crypto/eth2_keystore" }
account_utils = { path = "../common/account_utils" }
slashing_protection = { path = "../validator_client/slashing_protection" }
eth2 = { path = "../common/eth2" }
safe_arith = { path = "../consensus/safe_arith" }
slot_clock = { path = "../common/slot_clock" }
filesystem = { path = "../common/filesystem" }
sensitive_url = { path = "../common/sensitive_url" }
serde = { version = "1.0.116", features = ["derive"] }
serde_json = "1.0.58"
slog = { version = "2.5.2" }
validator_dir = { workspace = true }
tokio = { workspace = true }
eth2_keystore = { workspace = true }
account_utils = { workspace = true }
slashing_protection = { workspace = true }
eth2 = { workspace = true }
safe_arith = { workspace = true }
slot_clock = { workspace = true }
filesystem = { workspace = true }
sensitive_url = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
slog = { workspace = true }
[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 safe_arith::SafeArith;
use sensitive_url::SensitiveUrl;
use slog::Logger;
use slot_clock::{SlotClock, SystemTimeSlotClock};
use std::path::{Path, PathBuf};
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> =
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 no_wait = matches.is_present(NO_WAIT);
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,
no_wait,
no_confirmation,
genesis_state_url,
genesis_state_url_timeout,
env.core_context().log(),
))?;
Ok(())
@ -130,13 +120,10 @@ async fn publish_voluntary_exit<E: EthSpec>(
eth2_network_config: &Eth2NetworkConfig,
no_wait: bool,
no_confirmation: bool,
genesis_state_url: Option<String>,
genesis_state_url_timeout: Duration,
log: &Logger,
) -> Result<(), String> {
let genesis_data = get_geneisis_data(client).await?;
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")?;
// 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::path::PathBuf;
use std::str::FromStr;
use std::time::Duration;
use types::{Epoch, EthSpec, PublicKeyBytes, Slot};
pub const CMD: &str = "slashing-protection";
@ -82,24 +81,12 @@ pub fn cli_run<T: EthSpec>(
validator_base_dir: PathBuf,
) -> Result<(), String> {
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
.eth2_network_config
.ok_or("Unable to get testnet configuration from the environment")?;
let genesis_validators_root = eth2_network_config
.genesis_validators_root::<T>(
genesis_state_url.as_deref(),
genesis_state_url_timeout,
context.log(),
)?
.genesis_validators_root::<T>()?
.ok_or_else(|| "Unable to get genesis state, has genesis occurred?".to_string())?;
match matches.subcommand() {

View File

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

View File

@ -2,7 +2,7 @@
name = "beacon_chain"
version = "0.2.0"
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
[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
[dev-dependencies]
maplit = "1.0.2"
environment = { path = "../../lighthouse/environment" }
serde_json = "1.0.58"
maplit = { workspace = true }
environment = { workspace = true }
serde_json = { workspace = true }
[dependencies]
serde_json = "1.0.58"
eth2_network_config = { path = "../../common/eth2_network_config"}
merkle_proof = { path = "../../consensus/merkle_proof" }
store = { path = "../store" }
parking_lot = "0.12.0"
lazy_static = "1.4.0"
smallvec = "1.6.1"
lighthouse_metrics = { path = "../../common/lighthouse_metrics" }
operation_pool = { path = "../operation_pool" }
rayon = "1.4.1"
serde = "1.0.116"
serde_derive = "1.0.116"
ethereum_serde_utils = "0.5.0"
slog = { version = "2.5.2", features = ["max_level_trace"] }
sloggers = { version = "2.1.1", features = ["json"] }
slot_clock = { path = "../../common/slot_clock" }
ethereum_hashing = "1.0.0-beta.2"
ethereum_ssz = "0.5.0"
ssz_types = "0.5.4"
ethereum_ssz_derive = "0.5.3"
state_processing = { path = "../../consensus/state_processing" }
tree_hash_derive = "0.5.0"
tree_hash = "0.5.2"
types = { path = "../../consensus/types" }
tokio = "1.14.0"
tokio-stream = "0.1.3"
eth1 = { path = "../eth1" }
futures = "0.3.7"
genesis = { path = "../genesis" }
int_to_bytes = { path = "../../consensus/int_to_bytes" }
rand = "0.8.5"
proto_array = { path = "../../consensus/proto_array" }
lru = "0.7.1"
tempfile = "3.1.0"
bitvec = "0.20.4"
bls = { path = "../../crypto/bls" }
kzg = { path = "../../crypto/kzg" }
safe_arith = { path = "../../consensus/safe_arith" }
fork_choice = { path = "../../consensus/fork_choice" }
task_executor = { path = "../../common/task_executor" }
derivative = "2.1.1"
itertools = "0.10.0"
slasher = { path = "../../slasher" }
eth2 = { path = "../../common/eth2" }
strum = { version = "0.24.0", features = ["derive"] }
logging = { path = "../../common/logging" }
execution_layer = { path = "../execution_layer" }
sensitive_url = { path = "../../common/sensitive_url" }
superstruct = "0.5.0"
hex = "0.4.2"
exit-future = "0.2.0"
unused_port = {path = "../../common/unused_port"}
oneshot_broadcast = { path = "../../common/oneshot_broadcast" }
slog-term = "2.6.0"
slog-async = "2.5.0"
serde_json = { workspace = true }
eth2_network_config = { workspace = true }
merkle_proof = { workspace = true }
store = { workspace = true }
parking_lot = { workspace = true }
lazy_static = { workspace = true }
smallvec = { workspace = true }
lighthouse_metrics = { workspace = true }
operation_pool = { workspace = true }
rayon = { workspace = true }
serde = { workspace = true }
ethereum_serde_utils = { workspace = true }
slog = { workspace = true }
sloggers = { workspace = true }
slot_clock = { workspace = true }
ethereum_hashing = { workspace = true }
ethereum_ssz = { workspace = true }
ssz_types = { workspace = true }
ethereum_ssz_derive = { workspace = true }
state_processing = { workspace = true }
tree_hash_derive = { workspace = true }
tree_hash = { workspace = true }
types = { workspace = true }
tokio = { workspace = true }
tokio-stream = { workspace = true }
eth1 = { workspace = true }
futures = { workspace = true }
genesis = { workspace = true }
int_to_bytes = { workspace = true }
rand = { workspace = true }
proto_array = { workspace = true }
lru = { workspace = true }
tempfile = { workspace = true }
bitvec = { workspace = true }
bls = { workspace = true }
kzg = { workspace = true }
safe_arith = { workspace = true }
fork_choice = { workspace = true }
task_executor = { workspace = true }
derivative = { workspace = true }
itertools = { workspace = true }
slasher = { workspace = true }
eth2 = { workspace = true }
strum = { workspace = true }
logging = { workspace = true }
execution_layer = { workspace = true }
sensitive_url = { workspace = true }
superstruct = { workspace = true }
hex = { workspace = true }
exit-future = { workspace = true }
oneshot_broadcast = { path = "../../common/oneshot_broadcast/" }
slog-term = { workspace = true }
slog-async = { workspace = true }
[[test]]
name = "beacon_chain_tests"

View File

@ -874,10 +874,10 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
///
/// May return a database error.
pub fn state_root_at_slot(&self, request_slot: Slot) -> Result<Option<Hash256>, Error> {
if request_slot > self.slot()? {
return Ok(None);
} else if request_slot == self.spec.genesis_slot {
if request_slot == self.spec.genesis_slot {
return Ok(Some(self.genesis_state_root));
} else if request_slot > self.slot()? {
return Ok(None);
}
// Check limits w.r.t historic state bounds.
@ -954,10 +954,10 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
///
/// May return a database error.
fn block_root_at_slot_skips_none(&self, request_slot: Slot) -> Result<Option<Hash256>, Error> {
if request_slot > self.slot()? {
return Ok(None);
} else if request_slot == self.spec.genesis_slot {
if request_slot == self.spec.genesis_slot {
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);
@ -1017,10 +1017,10 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
///
/// May return a database error.
fn block_root_at_slot_skips_prev(&self, request_slot: Slot) -> Result<Option<Hash256>, Error> {
if request_slot > self.slot()? {
return Ok(None);
} else if request_slot == self.spec.genesis_slot {
if request_slot == self.spec.genesis_slot {
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.

View File

@ -1,4 +1,4 @@
use serde_derive::Serialize;
use serde::Serialize;
use std::sync::Arc;
use types::{
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
// check, we will load the parent and state from disk only to find out later that we
// already know this block.
if chain
.canonical_head
.fork_choice_read_lock()
.contains_block(&block_root)
{
let fork_choice_read_lock = chain.canonical_head.fork_choice_read_lock();
if fork_choice_read_lock.contains_block(&block_root) {
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.
let block = check_block_is_finalized_checkpoint_or_descendant(
chain,
&chain.canonical_head.fork_choice_read_lock(),
&fork_choice_read_lock,
block,
)?;
drop(fork_choice_read_lock);
let block_epoch = block.slot().epoch(T::EthSpec::slots_per_epoch());
let (parent_block, block) = verify_parent_block_is_known(chain, block)?;

View File

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

View File

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

View File

@ -1,8 +1,10 @@
//! Provides tools for checking if a node is ready for the Bellatrix upgrade and following merge
//! transition.
use crate::{BeaconChain, BeaconChainTypes};
use crate::{BeaconChain, BeaconChainError as Error, BeaconChainTypes};
use execution_layer::BlockByNumberQuery;
use serde::{Deserialize, Serialize, Serializer};
use slog::debug;
use std::fmt;
use std::fmt::Write;
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> {
/// Returns `true` if user has an EL configured, or if the Bellatrix fork has occurred or will
/// 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.
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 !el.is_synced_for_notifier().await {
if !el.is_synced_for_notifier(current_slot).await {
// The EL is not synced.
return MergeReadiness::NotSynced;
}
@ -161,6 +182,91 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
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.

View File

@ -20,8 +20,8 @@ use execution_layer::test_utils::generate_genesis_header;
use execution_layer::{
auth::JwtKey,
test_utils::{
ExecutionBlockGenerator, MockExecutionLayer, TestingBuilder, DEFAULT_JWT_SECRET,
DEFAULT_TERMINAL_BLOCK,
ExecutionBlockGenerator, MockBuilder, MockBuilderServer, MockExecutionLayer,
DEFAULT_JWT_SECRET, DEFAULT_TERMINAL_BLOCK,
},
ExecutionLayer,
};
@ -175,7 +175,6 @@ pub struct Builder<T: BeaconChainTypes> {
store_mutator: Option<BoxedMutator<T::EthSpec, T::HotStore, T::ColdStore>>,
execution_layer: Option<ExecutionLayer<T::EthSpec>>,
mock_execution_layer: Option<MockExecutionLayer<T::EthSpec>>,
mock_builder: Option<TestingBuilder<T::EthSpec>>,
testing_slot_clock: Option<TestingSlotClock>,
runtime: TestRuntime,
log: Logger,
@ -311,7 +310,6 @@ where
store_mutator: None,
execution_layer: None,
mock_execution_layer: None,
mock_builder: None,
testing_slot_clock: None,
runtime,
log,
@ -451,50 +449,21 @@ where
self
}
pub fn mock_execution_layer(mut self) -> Self {
pub fn mock_execution_layer(self) -> Self {
self.mock_execution_layer_with_config()
}
pub fn mock_execution_layer_with_config(mut self) -> Self {
let mock = mock_execution_layer_from_parts::<E>(
self.spec.as_ref().expect("cannot build without spec"),
self.runtime.task_executor.clone(),
None,
None,
);
self.execution_layer = Some(mock.el.clone());
self.mock_execution_layer = Some(mock);
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
/// asked to execute.
pub fn mock_execution_layer_all_payloads_valid(self) -> Self {
@ -581,7 +550,7 @@ where
shutdown_receiver: Arc::new(Mutex::new(shutdown_receiver)),
runtime: self.runtime,
mock_execution_layer: self.mock_execution_layer,
mock_builder: self.mock_builder.map(Arc::new),
mock_builder: None,
blob_signature_cache: <_>::default(),
rng: make_rng(),
}
@ -591,7 +560,6 @@ where
pub fn mock_execution_layer_from_parts<T: EthSpec>(
spec: &ChainSpec,
task_executor: TaskExecutor,
builder_url: Option<SensitiveUrl>,
builder_threshold: Option<u128>,
) -> MockExecutionLayer<T> {
let shanghai_time = spec.capella_fork_epoch.map(|epoch| {
@ -615,7 +583,6 @@ pub fn mock_execution_layer_from_parts<T: EthSpec>(
builder_threshold,
Some(JwtKey::from_slice(&DEFAULT_JWT_SECRET).unwrap()),
spec.clone(),
builder_url,
Some(kzg),
)
}
@ -639,7 +606,7 @@ pub struct BeaconChainHarness<T: BeaconChainTypes> {
pub runtime: TestRuntime,
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
/// to test gossip validation. We always make them during block production but drop them
@ -695,6 +662,49 @@ where
.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> {
let block = self.chain.head_beacon_block();
let block_root = block.canonical_root();

View File

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

View File

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

View File

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

View File

@ -259,7 +259,7 @@ where
"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))?
}
@ -279,7 +279,7 @@ where
.map_err(|e| format!("Unable to parse weak subj state SSZ: {:?}", e))?;
let anchor_block = SignedBeaconBlock::from_ssz_bytes(&anchor_block_bytes, &spec)
.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
.weak_subjectivity_state(anchor_state, anchor_block, genesis_state)
@ -380,7 +380,7 @@ where
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!(
context.log(),
@ -1099,7 +1099,7 @@ where
}
/// 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>,
config: &ClientConfig,
log: &Logger,
@ -1113,6 +1113,7 @@ fn genesis_state<T: EthSpec>(
config.genesis_state_url.as_deref(),
config.genesis_state_url_timeout,
log,
)?
)
.await?
.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
}
/// 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.
pub fn libp2p_listen_addresses(&self) -> Option<Vec<Multiaddr>> {
self.network_globals.as_ref().map(|n| n.listen_multiaddrs())

View File

@ -1,7 +1,7 @@
use crate::metrics;
use beacon_chain::{
capella_readiness::CapellaReadiness,
merge_readiness::{MergeConfig, MergeReadiness},
merge_readiness::{GenesisExecutionPayloadStatus, MergeConfig, MergeReadiness},
BeaconChain, BeaconChainTypes, ExecutionStatus,
};
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)),
);
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;
}
_ => break,
@ -365,7 +368,7 @@ async fn merge_readiness_logging<T: BeaconChainTypes>(
return;
}
match beacon_chain.check_merge_readiness().await {
match beacon_chain.check_merge_readiness(current_slot).await {
MergeReadiness::Ready {
config,
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) {
let current_slot_opt = beacon_chain.slot().ok();

View File

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

View File

@ -1,55 +1,58 @@
[package]
name = "execution_layer"
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
[dependencies]
types = { path = "../../consensus/types"}
tokio = { version = "1.10.0", features = ["full"] }
types = { workspace = true }
tokio = { workspace = true }
async-trait = "0.1.51"
slog = "2.5.2"
futures = "0.3.7"
sensitive_url = { path = "../../common/sensitive_url" }
reqwest = { version = "0.11.0", features = ["json","stream"] }
ethereum_serde_utils = "0.5.0"
serde_json = "1.0.58"
serde = { version = "1.0.116", features = ["derive"] }
warp = { version = "0.3.2", features = ["tls"] }
slog = { workspace = true }
futures = { workspace = true }
sensitive_url = { workspace = true }
reqwest = { workspace = true }
ethereum_serde_utils = { workspace = true }
serde_json = { workspace = true }
serde = { workspace = true }
warp = { workspace = true }
jsonwebtoken = "8"
environment = { path = "../../lighthouse/environment" }
bytes = "1.1.0"
task_executor = { path = "../../common/task_executor" }
hex = "0.4.2"
ethereum_ssz = "0.5.0"
ssz_types = "0.5.4"
eth2 = { path = "../../common/eth2" }
kzg = { path = "../../crypto/kzg" }
state_processing = { path = "../../consensus/state_processing" }
superstruct = "0.6.0"
lru = "0.7.1"
exit-future = "0.2.0"
tree_hash = "0.5.2"
tree_hash_derive = "0.5.0"
parking_lot = "0.12.0"
slot_clock = { path = "../../common/slot_clock" }
tempfile = "3.1.0"
rand = "0.8.5"
zeroize = { version = "1.4.2", features = ["zeroize_derive"] }
lighthouse_metrics = { path = "../../common/lighthouse_metrics" }
lazy_static = "1.4.0"
ethers-core = "1.0.2"
environment = { workspace = true }
bytes = { workspace = true }
task_executor = { workspace = true }
hex = { workspace = true }
ethereum_ssz = { workspace = true }
ssz_types = { workspace = true }
eth2 = { workspace = true }
kzg = { workspace = true }
state_processing = { workspace = true }
superstruct = { workspace = true }
lru = { workspace = true }
exit-future = { workspace = true }
tree_hash = { workspace = true }
tree_hash_derive = { workspace = true }
parking_lot = { workspace = true }
slot_clock = { workspace = true }
tempfile = { workspace = true }
rand = { workspace = true }
zeroize = { workspace = true }
lighthouse_metrics = { workspace = true }
lazy_static = { workspace = true }
ethers-core = { workspace = true }
builder_client = { path = "../builder_client" }
fork_choice = { path = "../../consensus/fork_choice" }
fork_choice = { workspace = true }
#PR: https://github.com/ralexstokes/mev-rs/pull/124
mev-rs = { git = "https://github.com/jimmygchen/mev-rs", rev = "dedc77a" }
axum = "0.6"
hyper = "0.14"
ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev = "12508c1f9b0c8f4bf4c5e9b6d441e840c1b37fd9" }
ssz_rs = "0.9.0"
tokio-stream = { version = "0.1.9", features = [ "sync" ] }
strum = "0.24.0"
tokio-stream = { workspace = true }
strum = { workspace = true }
keccak-hash = "0.10.0"
hash256-std-hasher = "0.15.2"
triehash = "0.8.4"
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.
use crate::payload_cache::PayloadCache;
use arc_swap::ArcSwapOption;
use auth::{strip_prefix, Auth, JwtKey};
use builder_client::BuilderHttpClient;
pub use engine_api::EngineCapabilities;
@ -301,7 +302,7 @@ type PayloadContentsRefTuple<'a, T> = (ExecutionPayloadRef<'a, T>, Option<&'a Bl
struct Inner<E: EthSpec> {
engine: Arc<Engine>,
builder: Option<BuilderHttpClient>,
builder: ArcSwapOption<BuilderHttpClient>,
execution_engine_forkchoice_lock: Mutex<()>,
suggested_fee_recipient: Option<Address>,
proposer_preparation_data: Mutex<HashMap<u64, ProposerPreparationDataEntry>>,
@ -453,25 +454,9 @@ impl<T: EthSpec> ExecutionLayer<T> {
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 {
engine: Arc::new(engine),
builder,
builder: ArcSwapOption::empty(),
execution_engine_forkchoice_lock: <_>::default(),
suggested_fee_recipient,
proposer_preparation_data: Mutex::new(HashMap::new()),
@ -486,19 +471,45 @@ impl<T: EthSpec> ExecutionLayer<T> {
last_new_payload_errored: RwLock::new(false),
};
Ok(Self {
let el = Self {
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> {
&self.inner.engine
}
pub fn builder(&self) -> &Option<BuilderHttpClient> {
&self.inner.builder
pub fn builder(&self) -> Option<Arc<BuilderHttpClient>> {
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
@ -655,9 +666,9 @@ impl<T: EthSpec> ExecutionLayer<T> {
///
/// 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
/// 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.
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;
if synced {
if let Ok(Some(block)) = self
@ -666,7 +677,7 @@ impl<T: EthSpec> ExecutionLayer<T> {
.get_block_by_number(BlockByNumberQuery::Tag(LATEST_TAG))
.await
{
if block.block_number == 0 {
if block.block_number == 0 && current_slot > 0 {
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(
&self,
hash: ExecutionBlockHash,

View File

@ -42,6 +42,11 @@ use types::{
ExecutionPayloadHeader, ForkName, ForkVersionedResponse, Hash256, Slot, Uint256,
};
pub type MockBuilderServer = axum::Server<
hyper::server::conn::AddrIncoming,
axum::routing::IntoMakeService<axum::routing::Router>,
>;
#[derive(Clone)]
pub enum Operation {
FeeRecipient(Address),
@ -166,19 +171,25 @@ impl BidStuff for BuilderBid {
}
}
pub struct TestingBuilder<E: EthSpec> {
server: BlindedBlockProviderServer<MockBuilder<E>>,
pub builder: MockBuilder<E>,
#[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> TestingBuilder<E> {
pub fn new(
impl<E: EthSpec> MockBuilder<E> {
pub fn new_for_testing(
mock_el_url: SensitiveUrl,
builder_url: SensitiveUrl,
beacon_url: SensitiveUrl,
spec: ChainSpec,
executor: TaskExecutor,
) -> Self {
) -> (Self, MockBuilderServer) {
let file = NamedTempFile::new().unwrap();
let path = file.path().into();
std::fs::write(&path, hex::encode(DEFAULT_JWT_SECRET)).unwrap();
@ -207,39 +218,13 @@ impl<E: EthSpec> TestingBuilder<E> {
spec,
context,
);
let port = builder_url.full.port().unwrap();
let host: Ipv4Addr = builder_url
.full
.host_str()
.unwrap()
.to_string()
.parse()
.unwrap();
let server = BlindedBlockProviderServer::new(host, port, builder.clone());
Self { server, builder }
let host: Ipv4Addr = Ipv4Addr::LOCALHOST;
let port = 0;
let provider = BlindedBlockProviderServer::new(host, port, builder.clone());
let server = provider.serve();
(builder, server)
}
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(
el: ExecutionLayer<E>,
beacon_client: BeaconNodeHttpClient,

View File

@ -34,7 +34,6 @@ impl<T: EthSpec> MockExecutionLayer<T> {
Some(JwtKey::from_slice(&DEFAULT_JWT_SECRET).unwrap()),
spec,
None,
None,
)
}
@ -47,7 +46,6 @@ impl<T: EthSpec> MockExecutionLayer<T> {
builder_threshold: Option<u128>,
jwt_key: Option<JwtKey>,
spec: ChainSpec,
builder_url: Option<SensitiveUrl>,
kzg: Option<Kzg<T::Kzg>>,
) -> Self {
let handle = executor.handle().unwrap();
@ -72,7 +70,6 @@ impl<T: EthSpec> MockExecutionLayer<T> {
let config = Config {
execution_endpoints: vec![url],
builder_url,
secret_files: vec![path],
suggested_fee_recipient: Some(Address::repeat_byte(42)),
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 mock_builder::{
convert_err, custom_err, from_ssz_rs, to_ssz_rs, Context as MockBuilderContext, MockBuilder,
Operation, TestingBuilder,
MockBuilderServer, Operation,
};
pub use mock_execution_layer::MockExecutionLayer;

View File

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

View File

@ -2,54 +2,53 @@
name = "http_api"
version = "0.1.0"
authors = ["Paul Hauner <paul@paulhauner.com>"]
edition = "2021"
edition = { workspace = true }
autotests = false # using a single test binary compiles faster
[dependencies]
warp = { version = "0.3.2", features = ["tls"] }
serde = { version = "1.0.116", features = ["derive"] }
tokio = { version = "1.14.0", features = ["macros", "sync"] }
tokio-stream = { version = "0.1.3", features = ["sync"] }
types = { path = "../../consensus/types" }
hex = "0.4.2"
beacon_chain = { path = "../beacon_chain" }
eth2 = { path = "../../common/eth2", features = ["lighthouse"] }
slog = "2.5.2"
network = { path = "../network" }
lighthouse_network = { path = "../lighthouse_network" }
eth1 = { path = "../eth1" }
state_processing = { path = "../../consensus/state_processing" }
lighthouse_version = { path = "../../common/lighthouse_version" }
lighthouse_metrics = { path = "../../common/lighthouse_metrics" }
lazy_static = "1.4.0"
warp_utils = { path = "../../common/warp_utils" }
slot_clock = { path = "../../common/slot_clock" }
ethereum_ssz = "0.5.0"
warp = { workspace = true }
serde = { workspace = true }
tokio = { workspace = true }
tokio-stream = { workspace = true }
types = { workspace = true }
hex = { workspace = true }
beacon_chain = { workspace = true }
eth2 = { workspace = true }
slog = { workspace = true }
network = { workspace = true }
lighthouse_network = { workspace = true }
eth1 = { workspace = true }
state_processing = { workspace = true }
lighthouse_version = { workspace = true }
lighthouse_metrics = { workspace = true }
lazy_static = { workspace = true }
warp_utils = { workspace = true }
slot_clock = { workspace = true }
ethereum_ssz = { workspace = true }
bs58 = "0.4.0"
futures = "0.3.8"
execution_layer = { path = "../execution_layer" }
parking_lot = "0.12.0"
safe_arith = { path = "../../consensus/safe_arith" }
task_executor = { path = "../../common/task_executor" }
lru = "0.7.7"
tree_hash = "0.5.2"
sysinfo = "0.26.5"
futures = { workspace = true }
execution_layer = { workspace = true }
parking_lot = { workspace = true }
safe_arith = { workspace = true }
task_executor = { workspace = true }
lru = { workspace = true }
tree_hash = { workspace = true }
sysinfo = { workspace = true }
system_health = { path = "../../common/system_health" }
directory = { path = "../../common/directory" }
logging = { path = "../../common/logging" }
ethereum_serde_utils = "0.5.0"
operation_pool = { path = "../operation_pool" }
sensitive_url = { path = "../../common/sensitive_url" }
unused_port = { path = "../../common/unused_port" }
store = { path = "../store" }
bytes = "1.1.0"
beacon_processor = { path = "../beacon_processor" }
directory = { workspace = true }
logging = { workspace = true }
ethereum_serde_utils = { workspace = true }
operation_pool = { workspace = true }
sensitive_url = { workspace = true }
store = { workspace = true }
bytes = { workspace = true }
beacon_processor = { workspace = true }
[dev-dependencies]
environment = { path = "../../lighthouse/environment" }
serde_json = "1.0.58"
proto_array = { path = "../../consensus/proto_array" }
genesis = { path = "../genesis" }
environment = { workspace = true }
serde_json = { workspace = true }
proto_array = { workspace = true }
genesis = { workspace = true }
[[test]]
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) {
let address = if let Some(socket_addr) = peer_info.seen_addresses().next() {
let mut addr = lighthouse_network::Multiaddr::from(socket_addr.ip());
addr.push(lighthouse_network::multiaddr::Protocol::Tcp(
socket_addr.port(),
));
addr.to_string()
let address = if let Some(multiaddr) = peer_info.seen_multiaddrs().next() {
multiaddr.to_string()
} else if let Some(addr) = peer_info.listening_addresses().first() {
addr.to_string()
} else {
@ -2922,13 +2918,8 @@ pub fn serve<T: BeaconChainTypes>(
.peers()
.for_each(|(peer_id, peer_info)| {
let address =
if let Some(socket_addr) = peer_info.seen_addresses().next() {
let mut addr =
lighthouse_network::Multiaddr::from(socket_addr.ip());
addr.push(lighthouse_network::multiaddr::Protocol::Tcp(
socket_addr.port(),
));
addr.to_string()
if let Some(multiaddr) = peer_info.seen_multiaddrs().next() {
multiaddr.to_string()
} else if let Some(addr) = peer_info.listening_addresses().first() {
addr.to_string()
} else {
@ -3055,6 +3046,7 @@ pub fn serve<T: BeaconChainTypes>(
.and(warp::path::end())
.and(not_while_syncing_filter.clone())
.and(warp::query::<api_types::ValidatorBlocksQuery>())
.and(warp::header::optional::<api_types::Accept>("accept"))
.and(task_spawner_filter.clone())
.and(chain_filter.clone())
.and(log_filter.clone())
@ -3062,6 +3054,7 @@ pub fn serve<T: BeaconChainTypes>(
|endpoint_version: EndpointVersion,
slot: Slot,
query: api_types::ValidatorBlocksQuery,
accept_header: Option<api_types::Accept>,
task_spawner: TaskSpawner<T::EthSpec>,
chain: Arc<BeaconChain<T>>,
log: Logger| {
@ -3109,9 +3102,24 @@ pub fn serve<T: BeaconChainTypes>(
let block_contents =
build_block_contents::build_block_contents(fork_name, block, maybe_blobs)?;
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))
match accept_header {
Some(api_types::Accept::Ssz) => Response::builder()
.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(not_while_syncing_filter.clone())
.and(warp::query::<api_types::ValidatorBlocksQuery>())
.and(warp::header::optional::<api_types::Accept>("accept"))
.and(task_spawner_filter.clone())
.and(chain_filter.clone())
.then(
|slot: Slot,
query: api_types::ValidatorBlocksQuery,
accept_header: Option<api_types::Accept>,
task_spawner: TaskSpawner<T::EthSpec>,
chain: Arc<BeaconChain<T>>| {
task_spawner.spawn_async_with_rejection(Priority::P0, async move {
@ -3176,10 +3186,25 @@ pub fn serve<T: BeaconChainTypes>(
maybe_blobs,
)?;
// 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))
match accept_header {
Some(api_types::Accept::Ssz) => Response::builder()
.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
))
}),
// 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
// task via a channel.
let builder_future = async move {
let builder = chain
let arc_builder = chain
.execution_layer
.as_ref()
.ok_or(BeaconChainError::ExecutionLayerMissing)
.map_err(warp_utils::reject::beacon_chain_error)?
.builder()
.builder();
let builder = arc_builder
.as_ref()
.ok_or(BeaconChainError::BuilderMissing)
.map_err(warp_utils::reject::beacon_chain_error)?;
@ -4363,7 +4389,8 @@ pub fn serve<T: BeaconChainTypes>(
.then(
|task_spawner: TaskSpawner<T::EthSpec>, chain: Arc<BeaconChain<T>>| {
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>(
warp::reply::json(&api_types::GenericResponse::from(merge_readiness))
.into_response(),

View File

@ -126,17 +126,9 @@ pub async fn create_api_server<T: BeaconChainTypes>(
test_runtime: &TestRuntime,
log: Logger,
) -> ApiServer<T::EthSpec, impl Future<Output = ()>> {
// Get a random unused port.
let port = unused_port::unused_tcp4_port().unwrap();
create_api_server_on_port(chain, test_runtime, log, port).await
}
// Use port 0 to allocate a new unused port.
let port = 0;
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();
// 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 network_globals = Arc::new(NetworkGlobals::new(
enr.clone(),
Some(TCP_PORT),
None,
meta_data,
vec![],
false,

View File

@ -10,15 +10,14 @@ use eth2::{
types::{BlockId as CoreBlockId, ForkChoiceNode, StateId as CoreStateId, *},
BeaconNodeHttpClient, Error, StatusCode, Timeouts,
};
use execution_layer::test_utils::TestingBuilder;
use execution_layer::test_utils::DEFAULT_BUILDER_THRESHOLD_WEI;
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::FutureExt;
use http_api::{
test_utils::{create_api_server, create_api_server_on_port, ApiServer},
test_utils::{create_api_server, ApiServer},
BlockId, StateId,
};
use lighthouse_network::{Enr, EnrExt, PeerId};
@ -73,7 +72,7 @@ struct ApiTester {
network_rx: NetworkReceivers<E>,
local_enr: Enr,
external_peer_id: PeerId,
mock_builder: Option<Arc<TestingBuilder<E>>>,
mock_builder: Option<Arc<MockBuilder<E>>>,
}
struct ApiTesterConfig {
@ -120,24 +119,28 @@ impl ApiTester {
}
pub async fn new_from_config(config: ApiTesterConfig) -> Self {
// Get a random unused port
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(
BeaconChainHarness::builder(MainnetEthSpec)
.spec(spec.clone())
.chain_config(ChainConfig {
reconstruct_historic_states: config.retain_historic_states,
..ChainConfig::default()
})
.logger(logging::test_logger())
.deterministic_keypairs(VALIDATOR_COUNT)
.fresh_ephemeral_store()
.mock_execution_layer_with_builder(beacon_url.clone(), config.builder_threshold)
.build(),
);
let mut harness = BeaconChainHarness::builder(MainnetEthSpec)
.spec(spec.clone())
.chain_config(ChainConfig {
reconstruct_historic_states: config.retain_historic_states,
..ChainConfig::default()
})
.logger(logging::test_logger())
.deterministic_keypairs(VALIDATOR_COUNT)
.fresh_ephemeral_store()
.mock_execution_layer_with_config()
.build();
harness
.mock_execution_layer
.as_ref()
.unwrap()
.server
.execution_block_generator()
.move_to_terminal_block()
.unwrap();
harness.advance_slot();
@ -247,29 +250,40 @@ impl ApiTester {
let ApiServer {
server,
listening_socket: _,
listening_socket,
network_rx,
local_enr,
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");
// 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(
beacon_url,
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 {
harness,
harness: Arc::new(harness),
chain,
client,
next_block,
@ -383,7 +397,6 @@ impl ApiTester {
.mock_builder
.as_ref()
.unwrap()
.builder
.add_operation(Operation::Value(Uint256::from(
DEFAULT_BUILDER_THRESHOLD_WEI,
)));
@ -406,7 +419,6 @@ impl ApiTester {
.mock_builder
.as_ref()
.unwrap()
.builder
.add_operation(Operation::Value(Uint256::from(
DEFAULT_BUILDER_PAYLOAD_VALUE_WEI,
)));
@ -2528,6 +2540,70 @@ impl ApiTester {
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 =
BeaconBlock::<E, FullPayload<E>>::from_ssz_bytes(&block_bytes, &self.chain.spec)
.expect("block bytes can be decoded");
let signed_block = block.sign(&sk, &fork, genesis_validators_root, &self.chain.spec);
self.client
.post_beacon_blocks_ssz(&signed_block)
.await
.unwrap();
assert_eq!(self.chain.head_beacon_block().as_ref(), &signed_block);
self.chain.slot_clock.set_slot(slot.as_u64() + 1);
}
self
}
pub async fn test_block_production_no_verify_randao(self) -> Self {
for _ in 0..E::slots_per_epoch() {
let slot = self.chain.slot().unwrap();
@ -2713,12 +2789,18 @@ impl ApiTester {
sk.sign(message).into()
};
let block_contents = self
let block_contents_bytes = self
.client
.get_validator_blinded_blocks::<E, Payload>(slot, &randao_reveal, None)
.get_validator_blinded_blocks_ssz::<E, Payload>(slot, &randao_reveal, None)
.await
.unwrap()
.data;
.expect("block bytes");
let block_contents = BlockContents::<E, Payload>::from_ssz_bytes(
&block_contents_bytes,
&self.chain.spec,
)
.expect("block bytes can be decoded");
let signed_block_contents =
block_contents.sign(&sk, &fork, genesis_validators_root, &self.chain.spec);
@ -3309,6 +3391,7 @@ impl ApiTester {
.unwrap()
.get_payload_by_root(&payload.tree_hash_root())
.is_none());
self
}
@ -3317,7 +3400,6 @@ impl ApiTester {
self.mock_builder
.as_ref()
.unwrap()
.builder
.add_operation(Operation::GasLimit(30_000_000));
let slot = self.chain.slot().unwrap();
@ -3361,7 +3443,6 @@ impl ApiTester {
self.mock_builder
.as_ref()
.unwrap()
.builder
.add_operation(Operation::FeeRecipient(test_fee_recipient));
let slot = self.chain.slot().unwrap();
@ -3404,7 +3485,6 @@ impl ApiTester {
self.mock_builder
.as_ref()
.unwrap()
.builder
.add_operation(Operation::ParentHash(invalid_parent_hash));
let slot = self.chain.slot().unwrap();
@ -3454,7 +3534,6 @@ impl ApiTester {
self.mock_builder
.as_ref()
.unwrap()
.builder
.add_operation(Operation::PrevRandao(invalid_prev_randao));
let slot = self.chain.slot().unwrap();
@ -3500,7 +3579,6 @@ impl ApiTester {
self.mock_builder
.as_ref()
.unwrap()
.builder
.add_operation(Operation::BlockNumber(invalid_block_number));
let slot = self.chain.slot().unwrap();
@ -3548,7 +3626,6 @@ impl ApiTester {
self.mock_builder
.as_ref()
.unwrap()
.builder
.add_operation(Operation::Timestamp(invalid_timestamp));
let slot = self.chain.slot().unwrap();
@ -3589,11 +3666,7 @@ impl ApiTester {
}
pub async fn test_payload_rejects_invalid_signature(self) -> Self {
self.mock_builder
.as_ref()
.unwrap()
.builder
.invalid_signatures();
self.mock_builder.as_ref().unwrap().invalid_signatures();
let slot = self.chain.slot().unwrap();
let epoch = self.chain.epoch().unwrap();
@ -3878,7 +3951,6 @@ impl ApiTester {
self.mock_builder
.as_ref()
.unwrap()
.builder
.add_operation(Operation::Value(Uint256::from(
DEFAULT_BUILDER_THRESHOLD_WEI - 1,
)));
@ -3916,7 +3988,6 @@ impl ApiTester {
self.mock_builder
.as_ref()
.unwrap()
.builder
.add_operation(Operation::Value(Uint256::from(
DEFAULT_MOCK_EL_PAYLOAD_VALUE_WEI + 1,
)));
@ -3954,7 +4025,6 @@ impl ApiTester {
self.mock_builder
.as_ref()
.unwrap()
.builder
.add_operation(Operation::Value(Uint256::from(
DEFAULT_MOCK_EL_PAYLOAD_VALUE_WEI,
)));
@ -3992,7 +4062,6 @@ impl ApiTester {
self.mock_builder
.as_ref()
.unwrap()
.builder
.add_operation(Operation::Value(Uint256::from(
DEFAULT_MOCK_EL_PAYLOAD_VALUE_WEI - 1,
)));
@ -4030,7 +4099,6 @@ impl ApiTester {
self.mock_builder
.as_ref()
.unwrap()
.builder
.add_operation(Operation::Value(Uint256::from(
DEFAULT_MOCK_EL_PAYLOAD_VALUE_WEI + 1,
)));
@ -4104,7 +4172,6 @@ impl ApiTester {
self.mock_builder
.as_ref()
.unwrap()
.builder
.add_operation(Operation::Value(Uint256::from(
DEFAULT_MOCK_EL_PAYLOAD_VALUE_WEI + 1,
)));
@ -4112,7 +4179,6 @@ impl ApiTester {
self.mock_builder
.as_ref()
.unwrap()
.builder
.add_operation(Operation::WithdrawalsRoot(Hash256::repeat_byte(0x42)));
let slot = self.chain.slot().unwrap();
@ -4976,6 +5042,20 @@ async fn block_production_verify_randao_invalid() {
.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)]
async fn blinded_block_production_full_payload_premerge() {
ApiTester::new()

View File

@ -2,25 +2,25 @@
name = "http_metrics"
version = "0.1.0"
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
[dependencies]
warp = "0.3.2"
serde = { version = "1.0.116", features = ["derive"] }
slog = "2.5.2"
beacon_chain = { path = "../beacon_chain" }
store = { path = "../store" }
lighthouse_network = { path = "../lighthouse_network" }
slot_clock = { path = "../../common/slot_clock" }
lighthouse_metrics = { path = "../../common/lighthouse_metrics" }
lighthouse_version = { path = "../../common/lighthouse_version" }
warp_utils = { path = "../../common/warp_utils" }
malloc_utils = { path = "../../common/malloc_utils" }
warp = { workspace = true }
serde = { workspace = true }
slog = { workspace = true }
beacon_chain = { workspace = true }
store = { workspace = true }
lighthouse_network = { workspace = true }
slot_clock = { workspace = true }
lighthouse_metrics = { workspace = true }
lighthouse_version = { workspace = true }
warp_utils = { workspace = true }
malloc_utils = { workspace = true }
[dev-dependencies]
tokio = { version = "1.14.0", features = ["sync"] }
reqwest = { version = "0.11.0", features = ["json"] }
environment = { path = "../../lighthouse/environment" }
types = { path = "../../consensus/types" }
tokio = { workspace = true }
reqwest = { workspace = true }
environment = { workspace = true }
types = { workspace = true }

View File

@ -2,63 +2,64 @@
name = "lighthouse_network"
version = "0.2.0"
authors = ["Sigma Prime <contact@sigmaprime.io>"]
edition = "2021"
edition = { workspace = true }
[dependencies]
discv5 = { version = "0.3.1", features = ["libp2p"] }
unsigned-varint = { version = "0.6.0", features = ["codec"] }
types = { path = "../../consensus/types" }
ssz_types = "0.5.4"
serde = { version = "1.0.116", features = ["derive"] }
serde_derive = "1.0.116"
ethereum_ssz = "0.5.0"
ethereum_ssz_derive = "0.5.3"
tree_hash = "0.5.2"
tree_hash_derive = "0.5.0"
slog = { version = "2.5.2", features = ["max_level_trace"] }
lighthouse_version = { path = "../../common/lighthouse_version" }
tokio = { version = "1.14.0", features = ["time", "macros"] }
futures = "0.3.7"
error-chain = "0.12.4"
dirs = "3.0.1"
fnv = "1.0.7"
lazy_static = "1.4.0"
lighthouse_metrics = { path = "../../common/lighthouse_metrics" }
smallvec = "1.6.1"
tokio-io-timeout = "1.1.1"
lru = "0.7.1"
lru_cache = { path = "../../common/lru_cache" }
parking_lot = "0.12.0"
sha2 = "0.10"
snap = "1.0.1"
hex = "0.4.2"
tokio-util = { version = "0.6.2", features = ["codec", "compat", "time"] }
tiny-keccak = "2.0.2"
task_executor = { path = "../../common/task_executor" }
rand = "0.8.5"
directory = { path = "../../common/directory" }
regex = "1.5.5"
strum = { version = "0.24.0", features = ["derive"] }
superstruct = "0.5.0"
discv5 = { workspace = true }
unsigned-varint = { version = "0.6", features = ["codec"] }
ssz_types = { workspace = true }
types = { workspace = true }
serde = { workspace = true }
serde_derive = "1"
ethereum_ssz = { workspace = true }
ethereum_ssz_derive = { workspace = true }
tree_hash = { workspace = true }
tree_hash_derive = { workspace = true }
slog = { workspace = true }
lighthouse_version = { workspace = true }
tokio = { workspace = true }
futures = { workspace = true }
error-chain = { workspace = true }
dirs = { workspace = true }
fnv = { workspace = true }
lazy_static = { workspace = true }
lighthouse_metrics = { workspace = true }
smallvec = { workspace = true }
tokio-io-timeout = "1"
lru = { workspace = true }
lru_cache = { workspace = true }
parking_lot = { workspace = true }
sha2 = { workspace = true }
snap = { workspace = true }
hex = { workspace = true }
tokio-util = { workspace = true }
tiny-keccak = "2"
task_executor = { workspace = true }
rand = { workspace = true }
directory = { workspace = true }
regex = { workspace = true }
strum = { workspace = true }
superstruct = { workspace = true }
prometheus-client = "0.21.0"
unused_port = { path = "../../common/unused_port" }
delay_map = "0.3.0"
unused_port = { workspace = true }
delay_map = { workspace = true }
void = "1"
libp2p-quic= { version = "0.9.2", features=["tokio"]}
libp2p-mplex = "0.40.0"
[dependencies.libp2p]
version = "0.52"
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]
slog-term = "2.6.0"
slog-async = "2.5.0"
tempfile = "3.1.0"
exit-future = "0.2.0"
void = "1"
quickcheck = "0.9.2"
quickcheck_macros = "0.9.1"
slog-term = { workspace = true }
slog-async = { workspace = true }
tempfile = { workspace = true }
exit-future = { workspace = true }
quickcheck = { workspace = true }
quickcheck_macros = { workspace = true }
[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.
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>,
/// 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>,
/// 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>,
/// 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>,
/// 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.
pub target_peers: usize,
@ -102,6 +108,9 @@ pub struct Config {
/// Disables the discovery protocol from starting.
pub disable_discovery: bool,
/// Disables quic support.
pub disable_quic_support: bool,
/// Attempt to construct external port mappings with UPnP.
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
/// adjusted accordingly to ensure addresses that are present in the enr are globally
/// 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 {
addr,
udp_port,
disc_port,
quic_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)
}
/// 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
/// 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 {
addr,
udp_port,
disc_port,
quic_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)
}
/// 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
/// globally reachable.
#[allow(clippy::too_many_arguments)]
pub fn set_ipv4_ipv6_listening_addresses(
&mut self,
v4_addr: Ipv4Addr,
tcp4_port: u16,
udp4_port: u16,
disc4_port: u16,
quic4_port: u16,
v6_addr: Ipv6Addr,
tcp6_port: u16,
udp6_port: u16,
disc6_port: u16,
quic6_port: u16,
) {
self.listen_addresses = ListenAddress::DualStack(
ListenAddr {
addr: v4_addr,
udp_port: udp4_port,
disc_port: disc4_port,
quic_port: quic4_port,
tcp_port: tcp4_port,
},
ListenAddr {
addr: v6_addr,
udp_port: udp6_port,
disc_port: disc6_port,
quic_port: quic6_port,
tcp_port: tcp6_port,
},
);
self.discv5_config.listen_config = discv5::ListenConfig::default()
.with_ipv4(v4_addr, udp4_port)
.with_ipv6(v6_addr, udp6_port);
.with_ipv4(v4_addr, disc4_port)
.with_ipv6(v6_addr, disc6_port);
self.discv5_config.table_filter = |enr| match (&enr.ip4(), &enr.ip6()) {
(None, None) => false,
@ -213,27 +241,32 @@ impl Config {
match listen_addr {
ListenAddress::V4(ListenAddr {
addr,
udp_port,
disc_port,
quic_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 {
addr,
udp_port,
disc_port,
quic_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(
ListenAddr {
addr: ip4addr,
udp_port: udp4_port,
disc_port: disc4_port,
quic_port: quic4_port,
tcp_port: tcp4_port,
},
ListenAddr {
addr: ip6addr,
udp_port: udp6_port,
disc_port: disc6_port,
quic_port: quic6_port,
tcp_port: tcp6_port,
},
) => 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 {
addr: Ipv4Addr::UNSPECIFIED,
udp_port: 9000,
disc_port: 9000,
quic_port: 9001,
tcp_port: 9000,
});
@ -305,10 +339,11 @@ impl Default for Config {
network_dir,
listen_addresses,
enr_address: (None, None),
enr_udp4_port: None,
enr_quic4_port: None,
enr_tcp4_port: None,
enr_udp6_port: None,
enr_quic6_port: None,
enr_tcp6_port: None,
target_peers: 50,
gs_config,
@ -320,6 +355,7 @@ impl Default for Config {
disable_peer_scoring: false,
client_version: lighthouse_version::version_with_platform(),
disable_discovery: false,
disable_quic_support: false,
upnp_enabled: true,
network_load: 3,
private: false,
@ -417,7 +453,7 @@ pub fn gossipsub_config(
// We use the first 8 bytes of SHA256(topic, data) for content addressing
let fast_gossip_message_id = |message: &gossipsub::RawMessage| {
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(
prefix: [u8; 4],

View File

@ -17,6 +17,8 @@ use std::path::Path;
use std::str::FromStr;
use types::{EnrForkId, EthSpec};
use super::enr_ext::{EnrExt, QUIC6_ENR_KEY, QUIC_ENR_KEY};
/// The ENR field specifying the fork id.
pub const ETH2_ENR_KEY: &str = "eth2";
/// 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>(
config: &NetworkConfig,
enable_tcp: bool,
enable_libp2p: bool,
) -> EnrBuilder<T> {
let mut builder = EnrBuilder::new("v4");
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);
}
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.
let tcp4_port = config
.enr_tcp4_port
@ -218,6 +241,9 @@ fn compare_enr(local_enr: &Enr, disk_enr: &Enr) -> bool {
// tcp ports must match
&& local_enr.tcp4() == disk_enr.tcp4()
&& 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
&& local_enr.get(ETH2_ENR_KEY) == disk_enr.get(ETH2_ENR_KEY)
// 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 tiny_keccak::{Hasher, Keccak};
pub const QUIC_ENR_KEY: &str = "quic";
pub const QUIC6_ENR_KEY: &str = "quic6";
/// Extend ENR for libp2p types.
pub trait EnrExt {
/// The libp2p `PeerId` for the record.
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.
fn multiaddr(&self) -> Vec<Multiaddr>;
@ -26,6 +29,15 @@ pub trait EnrExt {
/// Returns any multiaddrs that contain the TCP protocol.
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.
@ -49,7 +61,17 @@ impl EnrExt for Enr {
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.
fn multiaddr(&self) -> Vec<Multiaddr> {
let mut multiaddrs: Vec<Multiaddr> = Vec::new();
@ -59,6 +81,12 @@ impl EnrExt for Enr {
multiaddr.push(Protocol::Udp(udp));
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() {
let mut multiaddr: Multiaddr = ip.into();
@ -73,6 +101,13 @@ impl EnrExt for Enr {
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() {
let mut multiaddr: Multiaddr = ip6.into();
multiaddr.push(Protocol::Tcp(tcp6));
@ -174,8 +209,30 @@ impl EnrExt for Enr {
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`.
/// The vector remains empty if these fields are not defined.
fn multiaddr_tcp(&self) -> Vec<Multiaddr> {
let mut multiaddrs: Vec<Multiaddr> = Vec::new();
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 futures::prelude::*;
use futures::stream::FuturesUnordered;
use libp2p::multiaddr::Protocol;
use libp2p::swarm::behaviour::{DialFailure, FromSwarm};
use libp2p::swarm::THandlerInEvent;
pub use libp2p::{
@ -75,7 +74,7 @@ const DURATION_DIFFERENCE: Duration = Duration::from_millis(1);
/// of the peer if it is specified.
#[derive(Debug)]
pub struct DiscoveredPeers {
pub peers: HashMap<PeerId, Option<Instant>>,
pub peers: HashMap<Enr, Option<Instant>>,
}
#[derive(Clone, PartialEq)]
@ -208,7 +207,8 @@ impl<TSpec: EthSpec> Discovery<TSpec> {
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(),
"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
@ -230,7 +230,8 @@ impl<TSpec: EthSpec> Discovery<TSpec> {
"peer_id" => %bootnode_enr.peer_id(),
"ip" => ?bootnode_enr.ip4(),
"udp" => ?bootnode_enr.udp4(),
"tcp" => ?bootnode_enr.tcp4()
"tcp" => ?bootnode_enr.tcp4(),
"quic" => ?bootnode_enr.quic4()
);
let repr = bootnode_enr.to_string();
let _ = discv5.add_enr(bootnode_enr).map_err(|e| {
@ -281,7 +282,8 @@ impl<TSpec: EthSpec> Discovery<TSpec> {
"peer_id" => %enr.peer_id(),
"ip" => ?enr.ip4(),
"udp" => ?enr.udp4(),
"tcp" => ?enr.tcp4()
"tcp" => ?enr.tcp4(),
"quic" => ?enr.quic4()
);
let _ = discv5.add_enr(enr).map_err(|e| {
error!(
@ -383,20 +385,6 @@ impl<TSpec: EthSpec> Discovery<TSpec> {
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.
/// There currently isn't a case to update the address here. We opt for discovery to
/// automatically update the external address.
@ -414,6 +402,23 @@ impl<TSpec: EthSpec> Discovery<TSpec> {
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.
///
/// 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,
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() {
Ok(v) => v,
Err(e) => {
@ -773,7 +761,8 @@ impl<TSpec: EthSpec> Discovery<TSpec> {
// Build the future
let query_future = self
.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 {
query_type: query,
result: v,
@ -787,7 +776,7 @@ impl<TSpec: EthSpec> Discovery<TSpec> {
fn process_completed_queries(
&mut self,
query: QueryResult,
) -> Option<HashMap<PeerId, Option<Instant>>> {
) -> Option<HashMap<Enr, Option<Instant>>> {
match query.query_type {
QueryType::FindPeers => {
self.find_peer_active = false;
@ -797,12 +786,14 @@ impl<TSpec: EthSpec> Discovery<TSpec> {
}
Ok(r) => {
debug!(self.log, "Discovery query completed"; "peers_found" => r.len());
let mut results: HashMap<_, Option<Instant>> = HashMap::new();
r.iter().for_each(|enr| {
// cache the found ENR's
self.cached_enrs.put(enr.peer_id(), enr.clone());
results.insert(enr.peer_id(), None);
});
let results = r
.into_iter()
.map(|enr| {
// cache the found ENR's
self.cached_enrs.put(enr.peer_id(), enr.clone());
(enr, None)
})
.collect();
return Some(results);
}
Err(e) => {
@ -850,17 +841,17 @@ impl<TSpec: EthSpec> Discovery<TSpec> {
let subnet_predicate =
subnet_predicate::<TSpec>(vec![query.subnet], &self.log);
r.iter()
r.clone()
.into_iter()
.filter(|enr| subnet_predicate(enr))
.map(|enr| enr.peer_id())
.for_each(|peer_id| {
.for_each(|enr| {
if let Some(v) = metrics::get_int_counter(
&metrics::SUBNET_PEERS_FOUND,
&[query_str],
) {
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
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
(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
(Some(min_ttl), None) => {
mapped_results.insert(peer_id, Some(min_ttl));
}
// first seen min_ttl for this enr
(None, None) => {
mapped_results.insert(peer_id, None);
(min_ttl, None) => {
mapped_results.insert(enr, min_ttl);
}
(None, Some(Some(_))) => {} // Don't replace the existing specific min_ttl
(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.
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) {
let result = self.process_completed_queries(query_result);
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
fn poll(
&mut self,
@ -1047,25 +1017,8 @@ impl<TSpec: EthSpec> NetworkBehaviour for Discovery<TSpec> {
// update network globals
*self.network_globals.local_enr.write() = enr;
// A new UDP socket has been detected.
// Build a multiaddr to report to libp2p
let addr = match socket_addr.ip() {
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));
}
// NOTE: We assume libp2p itself can keep track of IP changes and we do
// not inform it about IP changes found via discovery.
}
Discv5Event::EnrAdded { .. }
| Discv5Event::TalkRequest(_)
@ -1152,8 +1105,6 @@ mod tests {
let log = build_log(slog::Level::Debug, false);
let globals = NetworkGlobals::new(
enr,
Some(9000),
None,
MetaData::V2(MetaDataV2 {
seq_number: 0,
attnets: Default::default(),
@ -1261,6 +1212,6 @@ mod tests {
assert_eq!(results.len(), 2);
// 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.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ListenAddr<Ip> {
/// The IP address we will listen on.
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,
}
impl<Ip: Into<IpAddr> + Clone> ListenAddr<Ip> {
pub fn udp_socket_addr(&self) -> SocketAddr {
(self.addr.clone().into(), self.udp_port).into()
pub fn discovery_socket_addr(&self) -> SocketAddr {
(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 {
@ -46,22 +55,41 @@ impl ListenAddress {
}
}
/// Returns the TCP addresses.
pub fn tcp_addresses(&self) -> impl Iterator<Item = Multiaddr> + '_ {
let v4_multiaddr = self
/// Returns the addresses the Swarm will listen on, given the setup.
pub fn libp2p_addresses(&self) -> impl Iterator<Item = Multiaddr> {
let v4_tcp_multiaddr = self
.v4()
.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()
.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)]
pub fn unused_v4_ports() -> Self {
ListenAddress::V4(ListenAddr {
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(),
})
}
@ -70,7 +98,8 @@ impl ListenAddress {
pub fn unused_v6_ports() -> Self {
ListenAddress::V6(ListenAddr {
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(),
})
}
@ -84,12 +113,14 @@ impl slog::KV for ListenAddress {
) -> slog::Result {
if let Some(v4_addr) = self.v4() {
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)?;
}
if let Some(v6_addr) = self.v6() {
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)?;
}
slog::Result::Ok(())

View File

@ -14,6 +14,16 @@ lazy_static! {
"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(
"libp2p_peer_connect_event_total",
"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.
use crate::discovery::enr_ext::EnrExt;
use crate::rpc::{GoodbyeReason, MetaData, Protocol, RPCError, RPCResponseErrorCode};
use crate::service::TARGET_SUBNET_PEERS;
use crate::{error, metrics, Gossipsub};
@ -13,7 +14,6 @@ use peerdb::{client::ClientKind, BanOperation, BanResult, ScoreUpdateResult};
use rand::seq::SliceRandom;
use slog::{debug, error, trace, warn};
use smallvec::SmallVec;
use std::collections::BTreeMap;
use std::{
sync::Arc,
time::{Duration, Instant},
@ -78,7 +78,7 @@ pub struct PeerManager<TSpec: EthSpec> {
/// The target number of peers we would like to connect to.
target_peers: usize,
/// 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
/// reconnection.
// 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
/// returned here.
///
/// NOTE: By dialing `PeerId`s and not multiaddrs, libp2p requests the multiaddr associated
/// 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.
/// This function decides whether or not to dial these peers.
#[allow(clippy::mutable_key_type)]
pub fn peers_discovered(&mut self, results: HashMap<PeerId, Option<Instant>>) -> Vec<PeerId> {
let mut to_dial_peers = Vec::with_capacity(4);
pub fn peers_discovered(&mut self, results: HashMap<Enr, Option<Instant>>) {
let mut to_dial_peers = 0;
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.
// 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
@ -330,10 +326,8 @@ impl<TSpec: EthSpec> PeerManager<TSpec> {
// 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
// if we are already at our max_peer limit.
if (min_ttl.is_some()
&& connected_or_dialing + to_dial_peers.len() < self.max_priority_peers()
|| connected_or_dialing + to_dial_peers.len() < self.max_peers())
&& self.network_globals.peers.read().should_dial(&peer_id)
if min_ttl.is_some() && connected_or_dialing + to_dial_peers < self.max_priority_peers()
|| connected_or_dialing + to_dial_peers < self.max_peers()
{
// This should be updated with the peer dialing. In fact created once the peer is
// dialed
@ -341,16 +335,16 @@ impl<TSpec: EthSpec> PeerManager<TSpec> {
self.network_globals
.peers
.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
self.maintain_peer_count(to_dial_peers.len());
to_dial_peers
self.maintain_peer_count(to_dial_peers);
}
/// 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 */
// A peer is being dialed.
pub fn dial_peer(&mut self, peer_id: &PeerId, enr: Option<Enr>) {
self.peers_to_dial.insert(*peer_id, enr);
/// A peer is being dialed.
pub fn dial_peer(&mut self, peer: 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.
@ -2208,7 +2209,7 @@ mod tests {
}
impl Arbitrary for PeerCondition {
fn arbitrary<G: Gen>(g: &mut G) -> Self {
fn arbitrary(g: &mut Gen) -> Self {
let attestation_net_bitfield = {
let len = <E as EthSpec>::SubnetBitfieldLength::to_usize();
let mut bitfield = Vec::with_capacity(len);

View File

@ -3,7 +3,7 @@
use std::task::{Context, Poll};
use futures::StreamExt;
use libp2p::core::ConnectedPoint;
use libp2p::core::{multiaddr, ConnectedPoint};
use libp2p::identity::PeerId;
use libp2p::swarm::behaviour::{ConnectionClosed, ConnectionEstablished, DialFailure, FromSwarm};
use libp2p::swarm::dial_opts::{DialOpts, PeerCondition};
@ -12,6 +12,7 @@ use libp2p::swarm::{ConnectionId, NetworkBehaviour, PollParameters, ToSwarm};
use slog::{debug, error};
use types::EthSpec;
use crate::discovery::enr_ext::EnrExt;
use crate::rpc::GoodbyeReason;
use crate::types::SyncState;
use crate::{metrics, ClearDialError};
@ -95,11 +96,23 @@ impl<TSpec: EthSpec> NetworkBehaviour for PeerManager<TSpec> {
self.events.shrink_to_fit();
}
if let Some((peer_id, maybe_enr)) = self.peers_to_dial.pop_first() {
self.inject_peer_connection(&peer_id, ConnectingType::Dialing, maybe_enr);
if let Some(enr) = self.peers_to_dial.pop() {
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 {
opts: DialOpts::peer_id(peer_id)
.condition(PeerCondition::Disconnected)
.addresses(multiaddrs)
.build(),
});
}
@ -124,9 +137,11 @@ impl<TSpec: EthSpec> NetworkBehaviour for PeerManager<TSpec> {
}
FromSwarm::ConnectionClosed(ConnectionClosed {
peer_id,
endpoint,
remaining_established,
..
}) => self.on_connection_closed(peer_id, remaining_established),
}) => self.on_connection_closed(peer_id, endpoint, remaining_established),
FromSwarm::DialFailure(DialFailure {
peer_id,
error,
@ -184,7 +199,11 @@ impl<TSpec: EthSpec> PeerManager<TSpec> {
endpoint: &ConnectedPoint,
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 {
self.events.push(PeerManagerEvent::MetaData(peer_id));
}
@ -194,6 +213,34 @@ impl<TSpec: EthSpec> PeerManager<TSpec> {
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
match self.ban_status(&peer_id) {
// TODO: directly emit the ban event?
@ -245,14 +292,15 @@ impl<TSpec: EthSpec> PeerManager<TSpec> {
self.events
.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 {
return;
}
@ -278,9 +326,31 @@ impl<TSpec: EthSpec> PeerManager<TSpec> {
// reference so that peer manager can track this peer.
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
self.update_connected_peer_metrics();
metrics::inc_counter(&metrics::PEER_DISCONNECT_EVENT_COUNT);
if self.metrics_enabled {
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.

View File

@ -1,16 +1,11 @@
use crate::{
metrics,
multiaddr::{Multiaddr, Protocol},
types::Subnet,
Enr, Gossipsub, PeerId,
};
use crate::{metrics, multiaddr::Multiaddr, types::Subnet, Enr, Gossipsub, PeerId};
use peer_info::{ConnectionDirection, PeerConnectionStatus, PeerInfo};
use rand::seq::SliceRandom;
use score::{PeerAction, ReportSource, Score, ScoreState};
use slog::{crit, debug, error, trace, warn};
use std::cmp::Ordering;
use std::collections::{HashMap, HashSet};
use std::net::{IpAddr, SocketAddr};
use std::net::IpAddr;
use std::time::Instant;
use sync_status::SyncStatus;
use types::EthSpec;
@ -764,28 +759,10 @@ impl<TSpec: EthSpec> PeerDB<TSpec> {
| 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
match direction {
ConnectionDirection::Incoming => info.connect_ingoing(socket_addr),
ConnectionDirection::Outgoing => info.connect_outgoing(socket_addr),
ConnectionDirection::Incoming => info.connect_ingoing(Some(seen_address)),
ConnectionDirection::Outgoing => info.connect_outgoing(Some(seen_address)),
}
}
@ -1274,6 +1251,7 @@ impl BannedPeersCount {
#[cfg(test)]
mod tests {
use super::*;
use libp2p::core::multiaddr::Protocol;
use libp2p::core::Multiaddr;
use slog::{o, Drain};
use std::net::{Ipv4Addr, Ipv6Addr};

View File

@ -2,15 +2,15 @@ use super::client::Client;
use super::score::{PeerAction, Score, ScoreState};
use super::sync_status::SyncStatus;
use crate::discovery::Eth2Enr;
use crate::Multiaddr;
use crate::{rpc::MetaData, types::Subnet};
use discv5::Enr;
use libp2p::core::multiaddr::{Multiaddr, Protocol};
use serde::{
ser::{SerializeStruct, Serializer},
Serialize,
};
use std::collections::HashSet;
use std::net::{IpAddr, SocketAddr};
use std::net::IpAddr;
use std::time::Instant;
use strum::AsRefStr;
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
/// (including local IPs).
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.
seen_addresses: HashSet<SocketAddr>,
seen_multiaddrs: HashSet<Multiaddr>,
/// The current syncing state of the peer. The state may be determined after it's initial
/// connection.
sync_status: SyncStatus,
@ -60,7 +60,7 @@ impl<TSpec: EthSpec> Default for PeerInfo<TSpec> {
client: Client::default(),
connection_status: Default::default(),
listening_addresses: Vec::new(),
seen_addresses: HashSet::new(),
seen_multiaddrs: HashSet::new(),
subnets: HashSet::new(),
sync_status: SyncStatus::Unknown,
meta_data: None,
@ -227,15 +227,21 @@ impl<T: EthSpec> PeerInfo<T> {
}
/// Returns the seen addresses of the peer.
pub fn seen_addresses(&self) -> impl Iterator<Item = &SocketAddr> + '_ {
self.seen_addresses.iter()
pub fn seen_multiaddrs(&self) -> impl Iterator<Item = &Multiaddr> + '_ {
self.seen_multiaddrs.iter()
}
/// Returns a list of seen IP addresses for the peer.
pub fn seen_ip_addresses(&self) -> impl Iterator<Item = IpAddr> + '_ {
self.seen_addresses
.iter()
.map(|socket_addr| socket_addr.ip())
self.seen_multiaddrs.iter().filter_map(|multiaddr| {
multiaddr.iter().find_map(|protocol| {
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.
@ -415,7 +421,7 @@ impl<T: EthSpec> PeerInfo<T> {
/// Modifies the status to Connected and increases the number of ingoing
/// 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 {
Connected { n_in, .. } => *n_in += 1,
Disconnected { .. }
@ -428,14 +434,14 @@ impl<T: EthSpec> PeerInfo<T> {
}
}
if let Some(socket_addr) = seen_address {
self.seen_addresses.insert(socket_addr);
if let Some(multiaddr) = seen_multiaddr {
self.seen_multiaddrs.insert(multiaddr);
}
}
/// Modifies the status to Connected and increases the number of outgoing
/// 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 {
Connected { n_out, .. } => *n_out += 1,
Disconnected { .. }
@ -447,8 +453,8 @@ impl<T: EthSpec> PeerInfo<T> {
self.connection_direction = Some(ConnectionDirection::Outgoing);
}
}
if let Some(ip_addr) = seen_address {
self.seen_addresses.insert(ip_addr);
if let Some(multiaddr) = seen_multiaddr {
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 globals = NetworkGlobals::new(
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,
config
.trusted_peers
@ -371,8 +369,9 @@ impl<AppReqId: ReqId, TSpec: EthSpec> Network<AppReqId, TSpec> {
let (swarm, bandwidth) = {
// Set up the transport - tcp/ws with noise and mplex
let (transport, bandwidth) = build_transport(local_keypair.clone())
.map_err(|e| format!("Failed to build transport: {:?}", e))?;
let (transport, bandwidth) =
build_transport(local_keypair.clone(), !config.disable_quic_support)
.map_err(|e| format!("Failed to build transport: {:?}", e))?;
// use the executor for libp2p
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<()> {
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));
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()) {
Ok(_) => {
let mut log_address = listen_multiaddr;
@ -470,6 +476,20 @@ impl<AppReqId: ReqId, TSpec: EthSpec> Network<AppReqId, TSpec> {
boot_nodes.dedup();
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() {
// ignore udp multiaddr if it exists
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.
fn dial_cached_enrs_in_subnet(&mut self, subnet: Subnet) {
let predicate = subnet_predicate::<TSpec>(vec![subnet], &self.log);
let peers_to_dial: Vec<PeerId> = self
let peers_to_dial: Vec<Enr> = self
.discovery()
.cached_enrs()
.filter_map(|(peer_id, enr)| {
let peers = self.network_globals.peers.read();
if predicate(enr) && peers.should_dial(peer_id) {
Some(*peer_id)
.filter_map(|(_peer_id, enr)| {
if predicate(enr) {
Some(enr.clone())
} else {
None
}
})
.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);
// 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);
// Remove the ENR from the cache to prevent continual re-dialing on disconnects
for enr in peers_to_dial {
debug!(self.log, "Dialing cached ENR peer"; "peer_id" => %enr.peer_id());
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.
fn inject_identify_event(
&mut self,
@ -1504,7 +1505,14 @@ impl<AppReqId: ReqId, TSpec: EthSpec> Network<AppReqId, TSpec> {
BehaviourEvent::BannedPeers(void) => void::unreachable(void),
BehaviourEvent::Gossipsub(ge) => self.inject_gs_event(ge),
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::PeerManager(pe) => self.inject_pm_event(pe),
BehaviourEvent::ConnectionLimits(le) => void::unreachable(le),
@ -1536,7 +1544,7 @@ impl<AppReqId: ReqId, TSpec: EthSpec> Network<AppReqId, TSpec> {
format!("Dialing local peer id {endpoint:?}")
}
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::TransportError::MultiaddrNotSupported(m) => {
@ -1586,13 +1594,7 @@ impl<AppReqId: ReqId, TSpec: EthSpec> Network<AppReqId, TSpec> {
None
}
}
SwarmEvent::Dialing {
peer_id,
connection_id: _,
} => {
debug!(self.log, "Swarm Dialing"; "peer_id" => ?peer_id);
None
}
SwarmEvent::Dialing { .. } => None,
};
if let Some(ev) = maybe_event {

View File

@ -4,11 +4,13 @@ use crate::types::{
error, EnrAttestationBitfield, EnrSyncCommitteeBitfield, GossipEncoding, GossipKind,
};
use crate::{GossipTopic, NetworkConfig};
use futures::future::Either;
use libp2p::bandwidth::BandwidthSinks;
use libp2p::core::{multiaddr::Multiaddr, muxing::StreamMuxerBox, transport::Boxed};
use libp2p::gossipsub;
use libp2p::identity::{secp256k1, Keypair};
use libp2p::{core, noise, yamux, PeerId, Transport, TransportExt};
use libp2p_quic;
use prometheus_client::registry::Registry;
use slog::{debug, warn};
use ssz::Decode;
@ -37,19 +39,12 @@ pub struct Context<'a> {
type BoxedTransport = Boxed<(PeerId, StreamMuxerBox)>;
/// The implementation supports TCP/IP, WebSockets over TCP/IP, noise as the encryption layer, and
/// mplex as the multiplexing layer.
/// The implementation supports TCP/IP, QUIC (experimental) over UDP, noise as the encryption layer, and
/// mplex/yamux as the multiplexing layer (when using TCP).
pub fn build_transport(
local_private_key: Keypair,
quic_support: bool,
) -> 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
let mut mplex_config = libp2p_mplex::MplexConfig::new();
mplex_config.set_max_buffer_size(256);
@ -58,18 +53,34 @@ pub fn build_transport(
// yamux config
let mut yamux_config = yamux::Config::default();
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)
.authenticate(generate_noise_config(&local_private_key))
.multiplex(core::upgrade::SelectUpgrade::new(
yamux_config,
mplex_config,
))
.timeout(Duration::from_secs(10))
.boxed()
.with_bandwidth_logging();
.timeout(Duration::from_secs(10));
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))
}

View File

@ -16,10 +16,6 @@ pub struct NetworkGlobals<TSpec: EthSpec> {
pub peer_id: RwLock<PeerId>,
/// Listening multiaddrs.
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.
pub peers: RwLock<PeerDB<TSpec>>,
// The local meta data of our node.
@ -35,8 +31,6 @@ pub struct NetworkGlobals<TSpec: EthSpec> {
impl<TSpec: EthSpec> NetworkGlobals<TSpec> {
pub fn new(
enr: Enr,
listen_port_tcp4: Option<u16>,
listen_port_tcp6: Option<u16>,
local_metadata: MetaData<TSpec>,
trusted_peers: Vec<PeerId>,
disable_peer_scoring: bool,
@ -46,8 +40,6 @@ impl<TSpec: EthSpec> NetworkGlobals<TSpec> {
local_enr: RwLock::new(enr.clone()),
peer_id: RwLock::new(enr.peer_id()),
listen_multiaddrs: RwLock::new(Vec::new()),
listen_port_tcp4,
listen_port_tcp6,
local_metadata: RwLock::new(local_metadata),
peers: RwLock::new(PeerDB::new(trusted_peers, disable_peer_scoring, log)),
gossipsub_subscriptions: RwLock::new(HashSet::new()),
@ -72,16 +64,6 @@ impl<TSpec: EthSpec> NetworkGlobals<TSpec> {
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.
pub fn connected_peers(&self) -> usize {
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();
NetworkGlobals::new(
enr,
Some(9000),
None,
MetaData::V2(MetaDataV2 {
seq_number: 0,
attnets: Default::default(),

View File

@ -13,7 +13,6 @@ use tokio::runtime::Runtime;
use types::{
ChainSpec, EnrForkId, Epoch, EthSpec, ForkContext, ForkName, Hash256, MinimalEthSpec, Slot,
};
use unused_port::unused_tcp4_port;
type E = MinimalEthSpec;
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();
// Find unused ports by using the 0 port.
let port = 0;
let random_path: u16 = rand::random();
let path = TempBuilder::new()
.prefix(&format!("libp2p_test{}", port))
.prefix(&format!("libp2p_test_{}", random_path))
.tempdir()
.unwrap();
config.set_ipv4_listening_address(std::net::Ipv4Addr::UNSPECIFIED, port, port);
config.enr_udp4_port = Some(port);
config.set_ipv4_listening_address(std::net::Ipv4Addr::UNSPECIFIED, port, port, port);
config.enr_address = (Some(std::net::Ipv4Addr::LOCALHOST), None);
config.boot_nodes_enr.append(&mut boot_nodes);
config.network_dir = path.into_path();
@ -99,8 +102,7 @@ pub async fn build_libp2p_instance(
fork_name: ForkName,
spec: &ChainSpec,
) -> Libp2pInstance {
let port = unused_tcp4_port().unwrap();
let config = build_config(port, boot_nodes);
let config = build_config(boot_nodes);
// launch libp2p service
let (signal, exit) = exit_future::signal();
@ -127,6 +129,12 @@ pub fn get_enr(node: &LibP2PService<ReqId, E>) -> 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.
// This returns a (sender, receiver) pair.
#[allow(dead_code)]
@ -135,6 +143,7 @@ pub async fn build_node_pair(
log: &slog::Logger,
fork_name: ForkName,
spec: &ChainSpec,
protocol: Protocol,
) -> (Libp2pInstance, Libp2pInstance) {
let sender_log = log.new(o!("who" => "sender"));
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 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 sender_fut = async {
loop {
if let NetworkEvent::NewListenAddr(_) = sender.next_event().await {
return;
if let NetworkEvent::NewListenAddr(addr) = sender.next_event().await {
// 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 {
loop {
if let NetworkEvent::NewListenAddr(_) = receiver.next_event().await {
return;
if let NetworkEvent::NewListenAddr(addr) = receiver.next_event().await {
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);
// wait for either both nodes to listen or a timeout
tokio::select! {
_ = tokio::time::sleep(Duration::from_millis(500)) => {}
_ = joined => {}
}
let receiver_multiaddr = joined.await.1;
// sender.dial_peer(peer_id);
match sender.testing_dial(receiver_multiaddr.clone()) {
Ok(()) => {
debug!(log, "Sender dialed receiver"; "address" => format!("{:?}", receiver_multiaddr))

View File

@ -1,4 +1,8 @@
#![cfg(test)]
mod common;
use common::Protocol;
use lighthouse_network::rpc::methods::*;
use lighthouse_network::{rpc::max_rpc_size, NetworkEvent, ReportSource, Request, Response};
use slog::{debug, warn, Level};
@ -14,8 +18,6 @@ use types::{
SignedBeaconBlock, Slot,
};
mod common;
type E = MinimalEthSpec;
/// 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
#[test]
#[allow(clippy::single_match)]
fn test_status_rpc() {
fn test_tcp_status_rpc() {
// set up the logging. The level and enabled logging or not
let log_level = Level::Debug;
let enable_logging = false;
@ -62,8 +64,14 @@ fn test_status_rpc() {
rt.block_on(async {
// get sender/receiver
let (mut sender, mut receiver) =
common::build_node_pair(Arc::downgrade(&rt), &log, ForkName::Base, &spec).await;
let (mut sender, mut receiver) = common::build_node_pair(
Arc::downgrade(&rt),
&log,
ForkName::Base,
&spec,
Protocol::Tcp,
)
.await;
// Dummy STATUS RPC message
let rpc_request = Request::Status(StatusMessage {
@ -141,7 +149,7 @@ fn test_status_rpc() {
// Tests a streamed BlocksByRange RPC Message
#[test]
#[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
let log_level = Level::Debug;
let enable_logging = false;
@ -156,8 +164,14 @@ fn test_blocks_by_range_chunked_rpc() {
rt.block_on(async {
// get sender/receiver
let (mut sender, mut receiver) =
common::build_node_pair(Arc::downgrade(&rt), &log, ForkName::Merge, &spec).await;
let (mut sender, mut receiver) = common::build_node_pair(
Arc::downgrade(&rt),
&log,
ForkName::Merge,
&spec,
Protocol::Tcp,
)
.await;
// BlocksByRange Request
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 {
// get sender/receiver
let spec = E::default_spec();
let (mut sender, mut receiver) =
common::build_node_pair(Arc::downgrade(&rt), &log, ForkName::Deneb, &spec).await;
let (mut sender, mut receiver) = common::build_node_pair(
Arc::downgrade(&rt),
&log,
ForkName::Deneb,
&spec,
Protocol::Tcp,
)
.await;
// BlobsByRange Request
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`.
#[test]
#[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
let log_level = Level::Debug;
let enable_logging = false;
@ -388,8 +408,14 @@ fn test_blocks_by_range_over_limit() {
rt.block_on(async {
// get sender/receiver
let (mut sender, mut receiver) =
common::build_node_pair(Arc::downgrade(&rt), &log, ForkName::Merge, &spec).await;
let (mut sender, mut receiver) = common::build_node_pair(
Arc::downgrade(&rt),
&log,
ForkName::Merge,
&spec,
Protocol::Tcp,
)
.await;
// BlocksByRange Request
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
#[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
let log_level = Level::Debug;
let enable_logging = false;
@ -472,8 +498,14 @@ fn test_blocks_by_range_chunked_rpc_terminates_correctly() {
rt.block_on(async {
// get sender/receiver
let (mut sender, mut receiver) =
common::build_node_pair(Arc::downgrade(&rt), &log, ForkName::Base, &spec).await;
let (mut sender, mut receiver) = common::build_node_pair(
Arc::downgrade(&rt),
&log,
ForkName::Base,
&spec,
Protocol::Tcp,
)
.await;
// BlocksByRange Request
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
#[test]
#[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
let log_level = Level::Trace;
let enable_logging = false;
@ -594,8 +626,14 @@ fn test_blocks_by_range_single_empty_rpc() {
rt.block_on(async {
// get sender/receiver
let (mut sender, mut receiver) =
common::build_node_pair(Arc::downgrade(&rt), &log, ForkName::Base, &spec).await;
let (mut sender, mut receiver) = common::build_node_pair(
Arc::downgrade(&rt),
&log,
ForkName::Base,
&spec,
Protocol::Tcp,
)
.await;
// BlocksByRange Request
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.
#[test]
#[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
let log_level = Level::Debug;
let enable_logging = false;
@ -695,8 +733,14 @@ fn test_blocks_by_root_chunked_rpc() {
let rt = Arc::new(Runtime::new().unwrap());
// get sender/receiver
rt.block_on(async {
let (mut sender, mut receiver) =
common::build_node_pair(Arc::downgrade(&rt), &log, ForkName::Merge, &spec).await;
let (mut sender, mut receiver) = common::build_node_pair(
Arc::downgrade(&rt),
&log,
ForkName::Merge,
&spec,
Protocol::Tcp,
)
.await;
// BlocksByRoot 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
#[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
let log_level = Level::Debug;
let enable_logging = false;
@ -822,8 +866,14 @@ fn test_blocks_by_root_chunked_rpc_terminates_correctly() {
let rt = Arc::new(Runtime::new().unwrap());
// get sender/receiver
rt.block_on(async {
let (mut sender, mut receiver) =
common::build_node_pair(Arc::downgrade(&rt), &log, ForkName::Base, &spec).await;
let (mut sender, mut receiver) = common::build_node_pair(
Arc::downgrade(&rt),
&log,
ForkName::Base,
&spec,
Protocol::Tcp,
)
.await;
// BlocksByRoot Request
let rpc_request =
@ -939,14 +989,9 @@ fn test_blocks_by_root_chunked_rpc_terminates_correctly() {
})
}
// Tests a Goodbye RPC message
#[test]
#[allow(clippy::single_match)]
fn test_goodbye_rpc() {
// set up the logging. The level and enabled logging or not
let log_level = Level::Trace;
let enable_logging = false;
/// Establishes a pair of nodes and disconnects the pair based on the selected protocol via an RPC
/// Goodbye message.
fn goodbye_test(log_level: Level, enable_logging: bool, protocol: Protocol) {
let log = common::build_log(log_level, enable_logging);
let rt = Arc::new(Runtime::new().unwrap());
@ -956,7 +1001,8 @@ fn test_goodbye_rpc() {
// get sender/receiver
rt.block_on(async {
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
let sender_future = async {
@ -982,12 +1028,9 @@ fn test_goodbye_rpc() {
// build the receiver future
let receiver_future = async {
loop {
match receiver.next_event().await {
NetworkEvent::PeerDisconnected(_) => {
// Should receive sent RPC request
return;
}
_ => {} // Ignore other events
if let NetworkEvent::PeerDisconnected(_) = receiver.next_event().await {
// Should receive sent RPC request
return;
}
}
};
@ -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"
version = "0.2.0"
authors = ["Sigma Prime <contact@sigmaprime.io>"]
edition = "2021"
edition = { workspace = true }
[dev-dependencies]
sloggers = { version = "2.1.1", features = ["json"] }
genesis = { path = "../genesis" }
sloggers = { workspace = true }
genesis = { workspace = true }
matches = "0.1.8"
exit-future = "0.2.0"
slog-term = "2.6.0"
slog-async = "2.5.0"
eth2 = {path="../../common/eth2"}
exit-future = { workspace = true }
slog-term = { workspace = true }
slog-async = { workspace = true }
eth2 = { workspace = true }
[dependencies]
beacon_chain = { path = "../beacon_chain" }
store = { path = "../store" }
lighthouse_network = { path = "../lighthouse_network" }
types = { path = "../../consensus/types" }
slot_clock = { path = "../../common/slot_clock" }
slog = { version = "2.5.2", features = ["max_level_trace", "nested-values"] }
hex = "0.4.2"
ethereum_ssz = "0.5.0"
ssz_types = "0.5.4"
futures = "0.3.7"
error-chain = "0.12.4"
tokio = { version = "1.14.0", features = ["full"] }
tokio-stream = "0.1.3"
smallvec = "1.6.1"
rand = "0.8.5"
fnv = "1.0.7"
beacon_chain = { workspace = true }
store = { workspace = true }
lighthouse_network = { workspace = true }
types = { workspace = true }
slot_clock = { workspace = true }
slog = { workspace = true }
hex = { workspace = true }
ethereum_ssz = { workspace = true }
ssz_types = { workspace = true }
futures = { workspace = true }
error-chain = { workspace = true }
tokio = { workspace = true }
tokio-stream = { workspace = true }
smallvec = { workspace = true }
rand = { workspace = true }
fnv = { workspace = true }
rlp = "0.5.0"
lazy_static = "1.4.0"
lighthouse_metrics = { path = "../../common/lighthouse_metrics" }
logging = { path = "../../common/logging" }
task_executor = { path = "../../common/task_executor" }
lazy_static = { workspace = true }
lighthouse_metrics = { workspace = true }
logging = { workspace = true }
task_executor = { workspace = true }
igd = "0.12.1"
itertools = "0.10.0"
lru_cache = { path = "../../common/lru_cache" }
itertools = { workspace = true }
num_cpus = { workspace = true }
lru_cache = { workspace = true }
if-addrs = "0.6.4"
strum = "0.24.0"
tokio-util = { version = "0.6.3", features = ["time"] }
derivative = "2.2.0"
delay_map = "0.3.0"
ethereum-types = { version = "0.14.1", optional = true }
operation_pool = { path = "../operation_pool" }
execution_layer = { path = "../execution_layer" }
beacon_processor = { path = "../beacon_processor" }
parking_lot = "0.12.0"
environment = { path = "../../lighthouse/environment" }
strum = { workspace = true }
tokio-util = { workspace = true }
derivative = { workspace = true }
delay_map = { workspace = true }
ethereum-types = { workspace = true }
operation_pool = { workspace = true }
execution_layer = { workspace = true }
beacon_processor = { workspace = true }
parking_lot = { workspace = true }
environment = { workspace = true }
[features]
# 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.
pub struct UPnPConfig {
/// The local tcp port.
/// The local TCP port.
tcp_port: u16,
/// The local udp port.
udp_port: u16,
/// The local UDP discovery port.
disc_port: u16,
/// The local UDP quic port.
quic_port: u16,
/// Whether discovery is enabled or not.
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 {
pub fn from_config(config: &NetworkConfig) -> Option<Self> {
config.listen_addrs().v4().map(|v4_addr| UPnPConfig {
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_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);
let mut mappings = EstablishedUPnPMappings::default();
match local_ip {
IpAddr::V4(address) => {
let libp2p_socket = SocketAddrV4::new(address, config.tcp_port);
@ -76,39 +107,46 @@ pub fn construct_upnp_mappings<T: EthSpec>(
// one.
// 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.
let tcp_socket = add_port_mapping(
mappings.tcp_port = add_port_mapping(
&gateway,
igd::PortMappingProtocol::TCP,
libp2p_socket,
"tcp",
&log,
).and_then(|_| {
).map(|_| {
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));
external_socket
config.tcp_port
}).ok();
let udp_socket = if !config.disable_discovery {
let discovery_socket = SocketAddrV4::new(address, config.udp_port);
let set_udp_mapping = |udp_port| {
let udp_socket = SocketAddrV4::new(address, udp_port);
add_port_mapping(
&gateway,
igd::PortMappingProtocol::UDP,
discovery_socket,
udp_socket,
"udp",
&log,
).and_then(|_| {
let external_socket = external_ip
.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
).map(|_| {
info!(log, "UPnP UDP route established"; "external_socket" => format!("{}:{}", external_ip.as_ref().map(|ip| ip.to_string()).unwrap_or_else(|_| "".into()), udp_port));
})
};
// 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.
network_send.send(NetworkMessage::UPnPMappingEstablished{ tcp_socket, udp_socket })
.unwrap_or_else(|e| debug!(log, "Could not send message to the network service"; "error" => %e));
if mappings.is_some() {
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"),
}
@ -161,12 +199,12 @@ fn add_port_mapping(
}
/// Removes the specified TCP and UDP port mappings.
pub fn remove_mappings(tcp_port: Option<u16>, udp_port: Option<u16>, log: &slog::Logger) {
if tcp_port.is_some() || udp_port.is_some() {
pub fn remove_mappings(mappings: &EstablishedUPnPMappings, log: &slog::Logger) {
if mappings.is_some() {
debug!(log, "Removing UPnP port mappings");
match igd::search_gateway(Default::default()) {
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) {
Ok(()) => debug!(log, "UPnP Removed TCP port mapping"; "port" => tcp_port),
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 {
match gateway.remove_port(igd::PortMappingProtocol::UDP, udp_port) {
for udp_port in mappings.udp_ports() {
match gateway.remove_port(igd::PortMappingProtocol::UDP, *udp_port) {
Ok(()) => debug!(log, "UPnP Removed UDP port mapping"; "port" => udp_port),
Err(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 LONG_CHAIN: u64 = SLOTS_PER_EPOCH * 2;
const TCP_PORT: u16 = 42;
const SEQ_NUMBER: u64 = 0;
/// The default time to wait for `BeaconProcessor` events.
@ -202,15 +201,7 @@ impl TestRig {
});
let enr_key = CombinedKey::generate_secp256k1();
let enr = EnrBuilder::new("v4").build(&enr_key).unwrap();
let network_globals = Arc::new(NetworkGlobals::new(
enr,
Some(TCP_PORT),
None,
meta_data,
vec![],
false,
&log,
));
let network_globals = Arc::new(NetworkGlobals::new(enr, meta_data, vec![], false, &log));
let executor = harness.runtime.task_executor.clone();

View File

@ -1,4 +1,5 @@
use super::sync::manager::RequestId as SyncId;
use crate::nat::EstablishedUPnPMappings;
use crate::network_beacon_processor::InvalidBlockStorage;
use crate::persisted_dht::{clear_dht, load_dht, persist_dht};
use crate::router::{Router, RouterMessage};
@ -26,7 +27,7 @@ use lighthouse_network::{
MessageId, NetworkEvent, NetworkGlobals, PeerId,
};
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 strum::IntoStaticStr;
use task_executor::ShutdownReason;
@ -93,12 +94,10 @@ pub enum NetworkMessage<T: EthSpec> {
/// The result of the validation
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 {
/// The external TCP address has been updated.
tcp_socket: Option<SocketAddr>,
/// The external UDP address has been updated.
udp_socket: Option<SocketAddr>,
/// The mappings that were established.
mappings: EstablishedUPnPMappings,
},
/// Reports a peer to the peer manager for performing an action.
ReportPeer {
@ -190,11 +189,8 @@ pub struct NetworkService<T: BeaconChainTypes> {
/// A collection of global variables, accessible outside of the network service.
network_globals: Arc<NetworkGlobals<T::EthSpec>>,
/// Stores potentially created UPnP mappings to be removed on shutdown. (TCP port and UDP
/// port).
upnp_mappings: (Option<u16>, Option<u16>),
/// 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,
/// ports).
upnp_mappings: EstablishedUPnPMappings,
/// A delay that expires when a new fork takes place.
next_fork_update: Pin<Box<OptionFuture<Sleep>>>,
/// 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,
store,
network_globals: network_globals.clone(),
upnp_mappings: (None, None),
discovery_auto_update: config.discv5_config.enr_update,
upnp_mappings: EstablishedUPnPMappings::default(),
next_fork_update,
next_fork_subscriptions,
next_unsubscribe,
@ -617,32 +612,18 @@ impl<T: BeaconChainTypes> NetworkService<T> {
} => {
self.libp2p.send_error_reponse(peer_id, id, error, reason);
}
NetworkMessage::UPnPMappingEstablished {
tcp_socket,
udp_socket,
} => {
self.upnp_mappings = (tcp_socket.map(|s| s.port()), udp_socket.map(|s| s.port()));
NetworkMessage::UPnPMappingEstablished { mappings } => {
self.upnp_mappings = mappings;
// If there is an external TCP port update, modify our local ENR.
if let Some(tcp_socket) = tcp_socket {
if let Err(e) = self
.libp2p
.discovery_mut()
.update_enr_tcp_port(tcp_socket.port())
{
if let Some(tcp_port) = self.upnp_mappings.tcp_port {
if let Err(e) = self.libp2p.discovery_mut().update_enr_tcp_port(tcp_port) {
warn!(self.log, "Failed to update ENR"; "error" => e);
}
}
// if the discovery service is not auto-updating, update it with the
// UPnP mappings
if !self.discovery_auto_update {
if let Some(udp_socket) = udp_socket {
if let Err(e) = self
.libp2p
.discovery_mut()
.update_enr_udp_socket(udp_socket)
{
warn!(self.log, "Failed to update ENR"; "error" => e);
}
// If there is an external QUIC port update, modify our local ENR.
if let Some(quic_port) = self.upnp_mappings.udp_quic_port {
if let Err(e) = self.libp2p.discovery_mut().update_enr_quic_port(quic_port) {
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
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");
}

View File

@ -60,7 +60,7 @@ mod tests {
);
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.upnp_enabled = false;
config.boot_nodes_enr = enrs.clone();

View File

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

View File

@ -1,4 +1,4 @@
use clap::{App, Arg};
use clap::{App, Arg, ArgGroup};
use strum::VariantNames;
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 \
over IpV4 and IpV6 set this flag twice with the different values.\n\
Examples:\n\
- --listen-address '0.0.0.0' will listen over Ipv4.\n\
- --listen-address '::' will listen over Ipv6.\n\
- --listen-address '0.0.0.0' will listen over IPv4.\n\
- --listen-address '::' will listen over IPv6.\n\
- --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, \
multiple Ipv4, or multiple Ipv6 addresses will not be accepted.")
IPv4 and IPv6. The order of the given addresses is not relevant. However, \
multiple IPv4, or multiple IPv6 addresses will not be accepted.")
.multiple(true)
.max_values(2)
.default_value("0.0.0.0")
@ -96,9 +96,10 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
Arg::with_name("port")
.long("port")
.value_name("PORT")
.help("The TCP/UDP port to listen on. The UDP port can be modified by the \
--discovery-port flag. If listening over both Ipv4 and Ipv6 the --port flag \
will apply to the Ipv4 address and --port6 to the Ipv6 address.")
.help("The TCP/UDP ports to listen on. There are two UDP ports. \
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 \
--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")
.takes_value(true),
)
@ -106,8 +107,8 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
Arg::with_name("port6")
.long("port6")
.value_name("PORT")
.help("The TCP/UDP port to listen on over IpV6 when listening over both Ipv4 and \
Ipv6. Defaults to 9090 when required.")
.help("The TCP/UDP ports to listen on over IPv6 when listening over both IPv4 and \
IPv6. Defaults to 9090 when required. The Quic UDP port will be set to this value + 1.")
.default_value("9090")
.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`")
.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::with_name("discovery-port6")
.long("discovery-port6")
.value_name("PORT")
.help("The UDP port that discovery will listen on over IpV6 if listening over \
both Ipv4 and IpV6. Defaults to `port6`")
.help("The UDP port that discovery will listen on over IPv6 if listening over \
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),
)
.arg(
@ -166,7 +182,15 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
.long("enr-udp-port")
.value_name("PORT")
.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),
)
.arg(
@ -174,7 +198,15 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
.long("enr-udp6-port")
.value_name("PORT")
.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),
)
.arg(
@ -182,7 +214,7 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
.long("enr-tcp-port")
.value_name("PORT")
.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.")
.takes_value(true),
)
@ -191,7 +223,7 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
.long("enr-tcp6-port")
.value_name("PORT")
.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.")
.takes_value(true),
)
@ -232,11 +264,18 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
without an ENR.")
.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::with_name("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.")
.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::with_name("disable-peer-scoring")
@ -323,22 +362,25 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
.arg(
Arg::with_name("http-address")
.long("http-address")
.requires("enable_http")
.value_name("ADDRESS")
.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),
)
.arg(
Arg::with_name("http-port")
.long("http-port")
.requires("enable_http")
.value_name("PORT")
.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),
)
.arg(
Arg::with_name("http-allow-origin")
.long("http-allow-origin")
.requires("enable_http")
.value_name("ORIGIN")
.help("Set the value of the Access-Control-Allow-Origin response HTTP header. \
Use * to allow any origin (not recommended in production). \
@ -349,11 +391,13 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
.arg(
Arg::with_name("http-disable-legacy-spec")
.long("http-disable-legacy-spec")
.requires("enable_http")
.hidden(true)
)
.arg(
Arg::with_name("http-spec-fork")
.long("http-spec-fork")
.requires("enable_http")
.value_name("FORK")
.help("Serve the spec for a specific hard fork on /eth/v1/config/spec. It should \
not be necessary to set this flag.")
@ -371,6 +415,7 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
.arg(
Arg::with_name("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 \
over TLS.")
.takes_value(true)
@ -378,6 +423,7 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
.arg(
Arg::with_name("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 \
over TLS. Must not be password-protected.")
.takes_value(true)
@ -385,6 +431,7 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
.arg(
Arg::with_name("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 \
stalled. This is useful for very small testnets. TESTING ONLY. DO NOT USE ON \
MAINNET.")
@ -392,8 +439,9 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
.arg(
Arg::with_name("http-sse-capacity-multiplier")
.long("http-sse-capacity-multiplier")
.requires("enable_http")
.takes_value(true)
.default_value("1")
.default_value_if("enable_http", None, "1")
.value_name("N")
.help("Multiplier to apply to the length of HTTP server-sent-event (SSE) channels. \
Increasing this value can prevent messages from being dropped.")
@ -401,8 +449,9 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
.arg(
Arg::with_name("http-duplicate-block-status")
.long("http-duplicate-block-status")
.requires("enable_http")
.takes_value(true)
.default_value("202")
.default_value_if("enable_http", None, "202")
.value_name("STATUS_CODE")
.help("Status code to send when a block that is already known is POSTed to the \
HTTP API.")
@ -410,13 +459,14 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
.arg(
Arg::with_name("http-enable-beacon-processor")
.long("http-enable-beacon-processor")
.requires("enable_http")
.value_name("BOOLEAN")
.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 \
alongside other tasks. When set to \"false\", HTTP API responses will be executed \
immediately.")
.takes_value(true)
.default_value("true")
.default_value_if("enable_http", None, "true")
)
/* Prometheus metrics HTTP server related arguments */
.arg(
@ -429,22 +479,25 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
Arg::with_name("metrics-address")
.long("metrics-address")
.value_name("ADDRESS")
.requires("metrics")
.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),
)
.arg(
Arg::with_name("metrics-port")
.long("metrics-port")
.requires("metrics")
.value_name("PORT")
.help("Set the listen TCP port for the Prometheus metrics HTTP server.")
.default_value("5054")
.default_value_if("metrics", None, "5054")
.takes_value(true),
)
.arg(
Arg::with_name("metrics-allow-origin")
.long("metrics-allow-origin")
.value_name("ORIGIN")
.requires("metrics")
.help("Set the value of the Access-Control-Allow-Origin response HTTP header. \
Use * to allow any origin (not recommended in production). \
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")
.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
*/
if cli_args.is_present("http") {
if cli_args.is_present("enable_http") {
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")? {
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}"))?
.unwrap_or(9090);
// parse the possible udp ports
let maybe_udp_port = cli_args
// parse the possible discovery ports.
let maybe_disc_port = cli_args
.value_of("discovery-port")
.map(str::parse::<u16>)
.transpose()
.map_err(|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")
.map(str::parse::<u16>)
.transpose()
@ -968,6 +968,24 @@ pub fn parse_listening_addresses(
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
let listening_addresses = match (maybe_ipv4, maybe_ipv6) {
(None, None) => {
@ -978,7 +996,7 @@ pub fn parse_listening_addresses(
// A single ipv6 address was provided. Set the ports
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.
let tcp_port = use_zero_ports
@ -986,20 +1004,32 @@ pub fn parse_listening_addresses(
.transpose()?
.unwrap_or(port);
if maybe_udp6_port.is_some() {
warn!(log, "When listening only over IpV6, use the --discovery-port flag. The value of --discovery-port6 will be ignored.")
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.")
}
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
// the tcp port.
let udp_port = use_zero_ports
let disc_port = use_zero_ports
.then(unused_port::unused_udp6_port)
.transpose()?
.or(maybe_udp_port)
.or(maybe_disc_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 {
addr: ipv6,
udp_port,
quic_port,
disc_port,
tcp_port,
})
}
@ -1011,16 +1041,25 @@ pub fn parse_listening_addresses(
.then(unused_port::unused_tcp4_port)
.transpose()?
.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.
let udp_port = use_zero_ports
let disc_port = use_zero_ports
.then(unused_port::unused_udp4_port)
.transpose()?
.or(maybe_udp_port)
.or(maybe_disc_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 {
addr: ipv4,
udp_port,
disc_port,
quic_port,
tcp_port,
})
}
@ -1029,31 +1068,44 @@ pub fn parse_listening_addresses(
.then(unused_port::unused_tcp4_port)
.transpose()?
.unwrap_or(port);
let ipv4_udp_port = use_zero_ports
let ipv4_disc_port = use_zero_ports
.then(unused_port::unused_udp4_port)
.transpose()?
.or(maybe_udp_port)
.or(maybe_disc_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
let ipv6_tcp_port = use_zero_ports
.then(unused_port::unused_tcp6_port)
.transpose()?
.unwrap_or(port6);
let ipv6_udp_port = use_zero_ports
let ipv6_disc_port = use_zero_ports
.then(unused_port::unused_udp6_port)
.transpose()?
.or(maybe_udp6_port)
.or(maybe_disc6_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(
lighthouse_network::ListenAddr {
addr: ipv4,
udp_port: ipv4_udp_port,
disc_port: ipv4_disc_port,
quic_port: ipv4_quic_port,
tcp_port: ipv4_tcp_port,
},
lighthouse_network::ListenAddr {
addr: ipv6,
udp_port: ipv6_udp_port,
disc_port: ipv6_disc_port,
quic_port: ipv6_quic_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") {
config.enr_tcp4_port = Some(
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") {
config.enr_tcp6_port = Some(
enr_tcp_port_str
@ -1194,9 +1262,9 @@ pub fn set_network_config(
}
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() {
let ipv4_enr_addr = if ipv4_addr.addr == Ipv4Addr::UNSPECIFIED {
Ipv4Addr::LOCALHOST
@ -1204,7 +1272,7 @@ pub fn set_network_config(
ipv4_addr.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() {
@ -1214,7 +1282,7 @@ pub fn set_network_config(
ipv6_addr.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.
let port = match config.listen_addrs() {
ListenAddress::V4(v4_addr) => v4_addr.udp_port,
ListenAddress::V6(v6_addr) => v6_addr.udp_port,
ListenAddress::V4(v4_addr) => v4_addr.disc_port,
ListenAddress::V6(v6_addr) => v6_addr.disc_port,
ListenAddress::DualStack(v4_addr, _v6_addr) => {
// 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");
}
if cli_args.is_present("disable-quic") {
config.disable_quic_support = true;
}
if cli_args.is_present("disable-upnp") {
config.upnp_enabled = false;
}

View File

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

View File

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

View File

@ -56,3 +56,4 @@
* [Contributing](./contributing.md)
* [Development Environment](./setup.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`.
- [`cmake`](https://cmake.org/cmake/help/latest/command/install.html). Used by
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.
- [`libpq-dev`](https://www.postgresql.org/docs/devel/libpq.html). Also know as
`libpq-devel` on some systems.

View File

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

View File

@ -25,7 +25,7 @@ pub struct BootNodeConfig<T: EthSpec> {
}
impl<T: EthSpec> BootNodeConfig<T> {
pub fn new(
pub async fn new(
matches: &ArgMatches<'_>,
eth2_network_config: &Eth2NetworkConfig,
) -> Result<Self, String> {
@ -58,12 +58,12 @@ impl<T: EthSpec> BootNodeConfig<T> {
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() {
network_config.enr_udp4_port = Some(
network_config
.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
.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() {
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(|| {
"The genesis state for this network is not known, this is an unsupported mode"
.to_string()

View File

@ -7,7 +7,7 @@ mod cli;
pub mod config;
mod server;
pub use cli::cli_app;
use config::{BootNodeConfig, BootNodeConfigSerialization};
use config::BootNodeConfig;
use types::{EthSpec, EthSpecId};
const LOG_CHANNEL_SIZE: usize = 2048;
@ -81,20 +81,13 @@ fn main<T: EthSpec>(
.build()
.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
if !lh_matches.is_present("immediate-shutdown") {
runtime.block_on(server::run(config, log));
}
runtime.block_on(server::run::<T>(
lh_matches,
bn_matches,
eth2_network_config,
log,
))?;
Ok(())
}

View File

@ -1,6 +1,9 @@
//! The main bootnode server execution.
use super::BootNodeConfig;
use crate::config::BootNodeConfigSerialization;
use clap::ArgMatches;
use eth2_network_config::Eth2NetworkConfig;
use lighthouse_network::{
discv5::{enr::NodeId, Discv5, Discv5Event},
EnrExt, Eth2Enr,
@ -8,7 +11,27 @@ use lighthouse_network::{
use slog::info;
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 {
boot_nodes,
local_enr,
@ -65,8 +88,7 @@ pub async fn run<T: EthSpec>(config: BootNodeConfig<T>, log: slog::Logger) {
// start the server
if let Err(e) = discv5.start().await {
slog::crit!(log, "Could not start discv5 server"; "error" => %e);
return;
return Err(format!("Could not start discv5 server: {e:?}"));
}
// 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 {
Ok(stream) => stream,
Err(e) => {
slog::crit!(log, "Failed to obtain event stream"; "error" => %e);
return;
return Err(format!("Failed to obtain event stream: {e:?}"));
}
};

View File

@ -2,22 +2,22 @@
name = "account_utils"
version = "0.1.0"
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
[dependencies]
rand = "0.8.5"
eth2_wallet = { path = "../../crypto/eth2_wallet" }
eth2_keystore = { path = "../../crypto/eth2_keystore" }
filesystem = { path = "../filesystem" }
zeroize = { version = "1.4.2", features = ["zeroize_derive"] }
serde = "1.0.116"
rand = { workspace = true }
eth2_wallet = { workspace = true }
eth2_keystore = { workspace = true }
filesystem = { workspace = true }
zeroize = { workspace = true }
serde = { workspace = true }
serde_derive = "1.0.116"
serde_yaml = "0.8.13"
slog = { version = "2.5.2", features = ["max_level_trace", "release_max_level_trace"] }
types = { path = "../../consensus/types" }
validator_dir = { path = "../validator_dir" }
regex = "1.5.5"
serde_yaml = { workspace = true }
slog = { workspace = true }
types = { workspace = true }
validator_dir = { workspace = true }
regex = { workspace = true }
rpassword = "5.0.0"
directory = { path = "../directory" }
directory = { workspace = true }

View File

@ -2,18 +2,18 @@
name = "clap_utils"
version = "0.1.0"
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
[dependencies]
clap = "2.33.3"
hex = "0.4.2"
dirs = "3.0.1"
eth2_network_config = { path = "../eth2_network_config" }
ethereum_ssz = "0.5.0"
ethereum-types = "0.14.1"
serde = "1.0.116"
serde_json = "1.0.59"
serde_yaml = "0.8.13"
types = { path = "../../consensus/types"}
clap = { workspace = true }
hex = { workspace = true }
dirs = { workspace = true }
eth2_network_config = { workspace = true }
ethereum_ssz = { workspace = true }
ethereum-types = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
serde_yaml = { workspace = true }
types = { workspace = true }

View File

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

View File

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

View File

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

View File

@ -2,11 +2,11 @@
name = "directory"
version = "0.1.0"
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
[dependencies]
clap = "2.33.3"
clap_utils = {path = "../clap_utils"}
eth2_network_config = { path = "../eth2_network_config" }
clap = { workspace = true }
clap_utils = { workspace = true }
eth2_network_config = { workspace = true }

View File

@ -2,38 +2,38 @@
name = "eth2"
version = "0.1.0"
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
[dependencies]
serde = { version = "1.0.116", features = ["derive"] }
serde_json = "1.0.58"
ssz_types = "0.5.4"
tree_hash = "0.5.2"
types = { path = "../../consensus/types" }
reqwest = { version = "0.11.0", features = ["json", "stream"] }
lighthouse_network = { path = "../../beacon_node/lighthouse_network" }
proto_array = { path = "../../consensus/proto_array", optional = true }
ethereum_serde_utils = "0.5.0"
eth2_keystore = { path = "../../crypto/eth2_keystore" }
libsecp256k1 = "0.7.0"
ring = "0.16.19"
bytes = "1.0.1"
account_utils = { path = "../../common/account_utils" }
sensitive_url = { path = "../../common/sensitive_url" }
ethereum_ssz = "0.5.0"
ethereum_ssz_derive = "0.5.3"
serde = { workspace = true }
serde_json = { workspace = true }
ssz_types = { workspace = true }
tree_hash = { workspace = true }
types = { workspace = true }
reqwest = { workspace = true }
lighthouse_network = { workspace = true }
proto_array = { workspace = true }
ethereum_serde_utils = { workspace = true }
eth2_keystore = { workspace = true }
libsecp256k1 = { workspace = true }
ring = { workspace = true }
bytes = { workspace = true }
account_utils = { workspace = true }
sensitive_url = { workspace = true }
ethereum_ssz = { workspace = true }
ethereum_ssz_derive = { workspace = true }
futures-util = "0.3.8"
futures = "0.3.8"
store = { path = "../../beacon_node/store", optional = true }
slashing_protection = { path = "../../validator_client/slashing_protection", optional = true }
futures = { workspace = true }
store = { workspace = true }
slashing_protection = { workspace = true }
mediatype = "0.19.13"
mime = "0.3.16"
pretty_reqwest_error = { path = "../../common/pretty_reqwest_error" }
pretty_reqwest_error = { workspace = true }
[dev-dependencies]
tokio = { version = "1.14.0", features = ["full"] }
tokio = { workspace = true }
[target.'cfg(target_os = "linux")'.dependencies]
psutil = { version = "3.2.2", optional = true }
@ -41,4 +41,4 @@ procfs = { version = "0.15.1", optional = true }
[features]
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_debug_beacon_states: Duration,
pub get_deposit_snapshot: Duration,
pub get_validator_block_ssz: Duration,
}
impl Timeouts {
@ -135,6 +136,7 @@ impl Timeouts {
get_beacon_blocks_ssz: timeout,
get_debug_beacon_states: timeout,
get_deposit_snapshot: timeout,
get_validator_block_ssz: timeout,
}
}
}
@ -1629,14 +1631,14 @@ impl BeaconNodeHttpClient {
.await
}
/// `GET v2/validator/blocks/{slot}`
pub async fn get_validator_blocks_modular<T: EthSpec, Payload: AbstractExecPayload<T>>(
/// returns `GET v2/validator/blocks/{slot}` URL path
pub async fn get_validator_blocks_path<T: EthSpec, Payload: AbstractExecPayload<T>>(
&self,
slot: Slot,
randao_reveal: &SignatureBytes,
graffiti: Option<&Graffiti>,
skip_randao_verification: SkipRandaoVerification,
) -> Result<ForkVersionedResponse<BlockContents<T, Payload>>, Error> {
) -> Result<Url, Error> {
let mut path = self.eth_path(V2)?;
path.path_segments_mut()
@ -1658,9 +1660,66 @@ impl BeaconNodeHttpClient {
.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
}
/// `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}`
pub async fn get_validator_blinded_blocks<T: EthSpec, Payload: AbstractExecPayload<T>>(
&self,
@ -1677,17 +1736,14 @@ impl BeaconNodeHttpClient {
.await
}
/// `GET v1/validator/blinded_blocks/{slot}`
pub async fn get_validator_blinded_blocks_modular<
T: EthSpec,
Payload: AbstractExecPayload<T>,
>(
/// returns `GET v1/validator/blinded_blocks/{slot}` URL path
pub async fn get_validator_blinded_blocks_path<T: EthSpec, Payload: AbstractExecPayload<T>>(
&self,
slot: Slot,
randao_reveal: &SignatureBytes,
graffiti: Option<&Graffiti>,
skip_randao_verification: SkipRandaoVerification,
) -> Result<ForkVersionedResponse<BlockContents<T, Payload>>, Error> {
) -> Result<Url, Error> {
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
@ -1709,9 +1765,71 @@ impl BeaconNodeHttpClient {
.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
}
/// `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`
pub async fn get_validator_attestation_data(
&self,

View File

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

View File

@ -1468,9 +1468,10 @@ mod tests {
}
/// A wrapper over a [`BeaconBlock`] or a [`BeaconBlockAndBlobSidecars`].
#[derive(Debug, Serialize, Deserialize)]
#[derive(Debug, Encode, Serialize, Deserialize)]
#[serde(untagged)]
#[serde(bound = "T: EthSpec")]
#[ssz(enum_behaviour = "transparent")]
pub enum BlockContents<T: EthSpec, Payload: AbstractExecPayload<T>> {
BlockAndBlobSidecars(BeaconBlockAndBlobSidecars<T, Payload>),
BlindedBlockAndBlobSidecars(BlindedBeaconBlockAndBlobSidecars<T, Payload>),

View File

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

View File

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

View File

@ -2,19 +2,19 @@
name = "eth2_interop_keypairs"
version = "0.2.0"
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
[dependencies]
lazy_static = "1.4.0"
lazy_static = { workspace = true }
num-bigint = "0.4.2"
ethereum_hashing = "1.0.0-beta.2"
hex = "0.4.2"
serde_yaml = "0.8.13"
serde = "1.0.116"
ethereum_hashing = { workspace = true }
hex = { workspace = true }
serde_yaml = { workspace = true }
serde = { workspace = true }
serde_derive = "1.0.116"
bls = { path = "../../crypto/bls" }
bls = { workspace = true }
[dev-dependencies]
base64 = "0.13.0"

View File

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

View File

@ -1,8 +1,9 @@
# EF
- enr:-Iq4QJk4WqRkjsX5c2CXtOra6HnxN-BMXnWhmhEQO9Bn9iABTJGdjUOurM7Btj1ouKaFkvTRoju5vz2GPmVON2dffQKGAX53x8JigmlkgnY0gmlwhLKAlv6Jc2VjcDI1NmsxoQK6S-Cii_KmfFdUJL2TANL3ksaKUnNXvTCv1tLwXs0QgIN1ZHCCIyk
- enr:-KG4QF6d6vMSboSujAXTI4vYqArccm0eIlXfcxf2Lx_VE1q6IkQo_2D5LAO3ZSBVUs0w5rrVDmABJZuMzISe_pZundADhGV0aDKQqX6DZjABcAAAAQAAAAAAAIJpZIJ2NIJpcISygIjpiXNlY3AyNTZrMaEDF3aSa7QSCvdqLpANNd8GML4PLEZVg45fKQwMWhDZjd2DdGNwgiMog3VkcIIjKA
- enr:-Ly4QJLXSSAj3ggPBIcodvBU6IyfpU_yW7E9J-5syoJorBuvcYj_Fokcjr303bQoTdWXADf8po0ssh75Mr5wVGzZZsMBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpCpfoNmMAFwAAABAAAAAAAAgmlkgnY0gmlwhJK-DYCJc2VjcDI1NmsxoQJrIlXIQDvQ6t9yDySqJYDXgZgLXzTvq8W7OI51jfmxJohzeW5jbmV0cwCDdGNwgiMog3VkcIIjKA
- enr:-Ku4QFo-9q73SspYI8cac_4kTX7yF800VXqJW4Lj3HkIkb5CMqFLxciNHePmMt4XdJzHvhrCC5ADI4D_GkAsxGJRLnQBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpAhnTT-AQFwAP__________gmlkgnY0gmlwhLKAiOmJc2VjcDI1NmsxoQORcM6e19T1T9gi7jxEZjk_sjVLGFscUNqAY9obgZaxbIN1ZHCCIyk
- enr:-Ku4QPG7F72mbKx3gEQEx07wpYYusGDh-ni6SNkLvOS-hhN-BxIggN7tKlmalb0L5JPoAfqD-akTZ-gX06hFeBEz4WoBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpAhnTT-AQFwAP__________gmlkgnY0gmlwhJK-DYCJc2VjcDI1NmsxoQKLVXFOhp2uX6jeT0DvvDpPcU8FWMjQdR4wMuORMhpX24N1ZHCCIyk
- enr:-LK4QPxe-mDiSOtEB_Y82ozvxn9aQM07Ui8A-vQHNgYGMMthfsfOabaaTHhhJHFCBQQVRjBww_A5bM1rf8MlkJU_l68Eh2F0dG5ldHOIAADAAAAAAACEZXRoMpBpt9l0BAFwAAABAAAAAAAAgmlkgnY0gmlwhLKAiOmJc2VjcDI1NmsxoQJu6T9pclPObAzEVQ53DpVQqjadmVxdTLL-J3h9NFoCeIN0Y3CCIyiDdWRwgiMo
- enr:-Ly4QGbOw4xNel5EhmDsJJ-QhC9XycWtsetnWoZ0uRy381GHdHsNHJiCwDTOkb3S1Ade0SFQkWJX_pgb3g8Jfh93rvMBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpBpt9l0BAFwAAABAAAAAAAAgmlkgnY0gmlwhJK-DYCJc2VjcDI1NmsxoQOxKv9sv3zKF8GDewgFGGHKP5HCZZpPpTrwl9eXKAWGxIhzeW5jbmV0cwCDdGNwgiMog3VkcIIjKA
# Teku
- enr:-LK4QMlzEff6d-M0A1pSFG5lJ2c56i_I-ZftdojZbW3ehkGNM4pkQuHQqzVvF1BG9aDjIakjnmO23mCBFFZ2w5zOsugEh2F0dG5ldHOIAAAAAAYAAACEZXRoMpCpfoNmMAFwAAABAAAAAAAAgmlkgnY0gmlwhKyuI_mJc2VjcDI1NmsxoQIH1kQRCZW-4AIVyAeXj5o49m_IqNFKRHp6tSpfXMUrSYN0Y3CCIyiDdWRwgiMo
- enr:-LS4QG0uV4qvcpJ-HFDJRGBmnlD3TJo7yc4jwK8iP7iKaTlfQ5kZvIDspLMJhk7j9KapuL9yyHaZmwTEZqr10k9XumyCEcmHYXR0bmV0c4gAAAAABgAAAIRldGgykGm32XQEAXAAAAEAAAAAAACCaWSCdjSCaXCErK4j-YlzZWNwMjU2azGhAgfWRBEJlb7gAhXIB5ePmjj2b8io0UpEenq1Kl9cxStJg3RjcIIjKIN1ZHCCIyg
# 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)
MIN_GENESIS_ACTIVE_VALIDATOR_COUNT: 16384
# Sep-15-2023 13:55:00 +UTC
MIN_GENESIS_TIME: 1694786100
GENESIS_FORK_VERSION: 0x00017000
# Sep-28-2023 11:55:00 +UTC
MIN_GENESIS_TIME: 1695902100
GENESIS_FORK_VERSION: 0x01017000
# Genesis delay 5 mins
GENESIS_DELAY: 300
@ -20,23 +20,19 @@ GENESIS_DELAY: 300
# - Temporarily set to max uint64 value: 2**64 - 1
# Altair
ALTAIR_FORK_VERSION: 0x10017000
ALTAIR_FORK_VERSION: 0x02017000
ALTAIR_FORK_EPOCH: 0
# Merge
BELLATRIX_FORK_VERSION: 0x20017000
BELLATRIX_FORK_VERSION: 0x03017000
BELLATRIX_FORK_EPOCH: 0
TERMINAL_TOTAL_DIFFICULTY: 0
TERMINAL_BLOCK_HASH: 0x0000000000000000000000000000000000000000000000000000000000000000
TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH: 18446744073709551615
# Capella
CAPELLA_FORK_VERSION: 0x30017000
CAPELLA_FORK_VERSION: 0x04017000
CAPELLA_FORK_EPOCH: 256
# DENEB
DENEB_FORK_VERSION: 0x40017000
DENEB_FORK_EPOCH: 18446744073709551615
# Time parameters
# ---------------------------------------------------------------
# 12 seconds

View File

@ -10,11 +10,13 @@
- enr:-Ku4QEWzdnVtXc2Q0ZVigfCGggOVB2Vc1ZCPEc6j21NIFLODSJbvNaef1g4PxhPwl_3kax86YPheFUSLXPRs98vvYsoBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpC1MD8qAAAAAP__________gmlkgnY0gmlwhDZBrP2Jc2VjcDI1NmsxoQM6jr8Rb1ktLEsVcKAPa08wCsKUmvoQ8khiOl_SLozf9IN1ZHCCIyg
# Teku team (Consensys)
- 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)
- enr:-Ku4QImhMc1z8yCiNJ1TyUxdcfNucje3BGwEHzodEZUan8PherEo4sF7pPHPSIB1NNuSg5fZy7qFsjmUKs2ea1Whi0EBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpD1pf1CAAAAAP__________gmlkgnY0gmlwhBLf22SJc2VjcDI1NmsxoQOVphkDqal4QzPMksc5wnpuC3gvSC8AfbFOnZY_On34wIN1ZHCCIyg
- enr:-Ku4QP2xDnEtUXIjzJ_DhlCRN9SN99RYQPJL92TMlSv7U5C1YnYLjwOQHgZIUXw6c-BvRg2Yc2QsZxxoS_pPRVe0yK8Bh2F0dG5ldHOIAAAAAAAAAACEZXRoMpD1pf1CAAAAAP__________gmlkgnY0gmlwhBLf22SJc2VjcDI1NmsxoQMeFF5GrS7UZpAH2Ly84aLK-TyvH-dRo0JM1i8yygH50YN1ZHCCJxA
- enr:-Ku4QPp9z1W4tAO8Ber_NQierYaOStqhDqQdOPY3bB3jDgkjcbk6YrEnVYIiCBbTxuar3CzS528d2iE7TdJsrL-dEKoBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpD1pf1CAAAAAP__________gmlkgnY0gmlwhBLf22SJc2VjcDI1NmsxoQMw5fqqkw2hHC4F5HZZDPsNmPdB1Gi8JPQK7pRc9XHh-oN1ZHCCKvg
# Nimbus team
- 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`
//! crate.
use bytes::Bytes;
use discv5::enr::{CombinedKey, Enr};
use eth2_config::{instantiate_hardcoded_nets, HardcodedNet};
use kzg::{KzgPreset, KzgPresetId, TrustedSetup};
use pretty_reqwest_error::PrettyReqwestError;
use reqwest::blocking::Client;
use reqwest::{Client, Error};
use sensitive_url::SensitiveUrl;
use sha2::{Digest, Sha256};
use slog::{info, warn, Logger};
@ -168,14 +169,8 @@ impl Eth2NetworkConfig {
self.genesis_state_source != GenesisStateSource::Unknown
}
/// The `genesis_validators_root` of the genesis state. May download the
/// genesis state if the value is not already available.
pub fn genesis_validators_root<E: EthSpec>(
&self,
genesis_state_url: Option<&str>,
timeout: Duration,
log: &Logger,
) -> Result<Option<Hash256>, String> {
/// The `genesis_validators_root` of the genesis state.
pub fn genesis_validators_root<E: EthSpec>(&self) -> Result<Option<Hash256>, String> {
if let GenesisStateSource::Url {
genesis_validators_root,
..
@ -190,10 +185,8 @@ impl Eth2NetworkConfig {
)
})
} else {
self.genesis_state::<E>(genesis_state_url, timeout, log)?
.map(|state| state.genesis_validators_root())
.map(Result::Ok)
.transpose()
self.get_genesis_state_from_bytes::<E>()
.map(|state| Some(state.genesis_validators_root()))
}
}
@ -211,7 +204,7 @@ impl Eth2NetworkConfig {
///
/// 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.
pub fn genesis_state<E: EthSpec>(
pub async fn genesis_state<E: EthSpec>(
&self,
genesis_state_url: Option<&str>,
timeout: Duration,
@ -221,15 +214,7 @@ impl Eth2NetworkConfig {
match &self.genesis_state_source {
GenesisStateSource::Unknown => Ok(None),
GenesisStateSource::IncludedBytes => {
let state = 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")??;
let state = self.get_genesis_state_from_bytes()?;
Ok(Some(state))
}
GenesisStateSource::Url {
@ -241,9 +226,9 @@ impl Eth2NetworkConfig {
format!("Unable to parse genesis state bytes checksum: {:?}", e)
})?;
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 {
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| {
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.
///
/// 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
/// are defined. Return `Ok` if any url returns a response that matches the
/// given `checksum`.
fn download_genesis_state(
async fn download_genesis_state(
urls: &[&str],
timeout: Duration,
checksum: Hash256,
@ -428,12 +424,7 @@ fn download_genesis_state(
);
let client = Client::new();
let response = client
.get(url)
.header("Accept", "application/octet-stream")
.timeout(timeout)
.send()
.and_then(|r| r.error_for_status().and_then(|r| r.bytes()));
let response = get_state_bytes(timeout, url, client).await;
match response {
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.
fn parse_state_download_url(url: &str) -> Result<Url, String> {
Url::parse(url)
@ -507,11 +510,12 @@ mod tests {
assert_eq!(spec, config.chain_spec::<GnosisEthSpec>().unwrap());
}
#[test]
fn mainnet_genesis_state() {
#[tokio::test]
async fn mainnet_genesis_state() {
let config = Eth2NetworkConfig::from_hardcoded_net(&MAINNET).unwrap();
config
.genesis_state::<E>(None, Duration::from_secs(1), &logging::test_logger())
.await
.expect("beacon state can decode");
}

View File

@ -2,13 +2,13 @@
name = "eth2_wallet_manager"
version = "0.1.0"
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
[dependencies]
eth2_wallet = { path = "../../crypto/eth2_wallet" }
lockfile = { path = "../lockfile" }
eth2_wallet = { workspace = true }
lockfile = { workspace = true }
[dev-dependencies]
tempfile = "3.1.0"
tempfile = { workspace = true }

View File

@ -2,7 +2,7 @@
name = "filesystem"
version = "0.1.0"
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

View File

@ -2,10 +2,10 @@
name = "lighthouse_metrics"
version = "0.2.0"
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
[dependencies]
lazy_static = "1.4.0"
lazy_static = { workspace = true }
prometheus = "0.13.0"

View File

@ -2,7 +2,7 @@
name = "lighthouse_version"
version = "0.1.0"
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
@ -11,4 +11,4 @@ git-version = "0.3.4"
target_info = "0.1.0"
[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
"--match=thiswillnevermatchlol"
],
prefix = "Lighthouse/v4.4.1-",
fallback = "Lighthouse/v4.4.1"
prefix = "Lighthouse/v4.5.0-",
fallback = "Lighthouse/v4.5.0"
);
/// Returns `VERSION`, but with platform information appended to the end.

View File

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

View File

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

View File

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

View File

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

View File

@ -2,22 +2,22 @@
name = "monitoring_api"
version = "0.1.0"
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
[dependencies]
reqwest = { version = "0.11.0", features = ["json","stream"] }
task_executor = { path = "../task_executor" }
tokio = "1.14.0"
eth2 = {path = "../eth2"}
serde_json = "1.0.58"
serde = "1.0.116"
reqwest = { workspace = true }
task_executor = { workspace = true }
tokio = { workspace = true }
eth2 = { workspace = true }
serde_json = { workspace = true }
serde_derive = "1.0.116"
lighthouse_version = { path = "../lighthouse_version"}
lighthouse_metrics = { path = "../lighthouse_metrics" }
slog = "2.5.2"
store = { path = "../../beacon_node/store" }
lazy_static = "1.4.0"
regex = "1.5.5"
sensitive_url = { path = "../sensitive_url" }
serde = { workspace = true }
lighthouse_version = { workspace = true }
lighthouse_metrics = { workspace = true }
slog = { workspace = true }
store = { workspace = true }
lazy_static = { workspace = true }
regex = { workspace = true }
sensitive_url = { workspace = true }

View File

@ -1,9 +1,9 @@
[package]
name = "oneshot_broadcast"
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
[dependencies]
parking_lot = "0.12.0"
parking_lot = { workspace = true }

View File

@ -1,10 +1,10 @@
[package]
name = "pretty_reqwest_error"
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
[dependencies]
reqwest = { version = "0.11.0", features = ["json","stream"] }
sensitive_url = { path = "../sensitive_url" }
reqwest = { workspace = true }
sensitive_url = { workspace = true }

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