Merge branch 'unstable' of https://github.com/sigp/lighthouse into deneb-free-blobs

This commit is contained in:
realbigsean 2023-06-02 11:57:15 -04:00
commit a227959298
No known key found for this signature in database
GPG Key ID: BE1B3DB104F6C788
63 changed files with 1689 additions and 1193 deletions

View File

@ -1,66 +0,0 @@
name: Publish Crate
on:
push:
tags:
- tree-hash-v*
- tree-hash-derive-v*
- eth2-ssz-v*
- eth2-ssz-derive-v*
- eth2-ssz-types-v*
- eth2-serde-util-v*
- eth2-hashing-v*
env:
CARGO_API_TOKEN: ${{ secrets.CARGO_API_TOKEN }}
jobs:
extract-tag:
runs-on: ubuntu-latest
steps:
- name: Extract tag
run: echo "TAG=$(echo ${GITHUB_REF#refs/tags/})" >> $GITHUB_OUTPUT
id: extract_tag
outputs:
TAG: ${{ steps.extract_tag.outputs.TAG }}
publish-crate:
runs-on: ubuntu-latest
needs: [extract-tag]
env:
TAG: ${{ needs.extract-tag.outputs.TAG }}
steps:
- uses: actions/checkout@v3
- name: Update Rust
run: rustup update stable
- name: Cargo login
run: |
echo "${CARGO_API_TOKEN}" | cargo login
- name: publish eth2 ssz derive
if: startsWith(env.TAG, 'eth2-ssz-derive-v')
run: |
./scripts/ci/publish.sh consensus/ssz_derive eth2_ssz_derive "$TAG"
- name: publish eth2 ssz
if: startsWith(env.TAG, 'eth2-ssz-v')
run: |
./scripts/ci/publish.sh consensus/ssz eth2_ssz "$TAG"
- name: publish eth2 hashing
if: startsWith(env.TAG, 'eth2-hashing-v')
run: |
./scripts/ci/publish.sh crypto/eth2_hashing eth2_hashing "$TAG"
- name: publish tree hash derive
if: startsWith(env.TAG, 'tree-hash-derive-v')
run: |
./scripts/ci/publish.sh consensus/tree_hash_derive tree_hash_derive "$TAG"
- name: publish tree hash
if: startsWith(env.TAG, 'tree-hash-v')
run: |
./scripts/ci/publish.sh consensus/tree_hash tree_hash "$TAG"
- name: publish ssz types
if: startsWith(env.TAG, 'eth2-ssz-types-v')
run: |
./scripts/ci/publish.sh consensus/ssz_types eth2_ssz_types "$TAG"
- name: publish serde util
if: startsWith(env.TAG, 'eth2-serde-util-v')
run: |
./scripts/ci/publish.sh consensus/serde_utils eth2_serde_utils "$TAG"

View File

@ -10,7 +10,7 @@ An open-source Ethereum consensus client, written in Rust and maintained by Sigm
[Book Link]: https://lighthouse-book.sigmaprime.io [Book Link]: https://lighthouse-book.sigmaprime.io
[stable]: https://github.com/sigp/lighthouse/tree/stable [stable]: https://github.com/sigp/lighthouse/tree/stable
[unstable]: https://github.com/sigp/lighthouse/tree/unstable [unstable]: https://github.com/sigp/lighthouse/tree/unstable
[blog]: https://lighthouse.sigmaprime.io [blog]: https://lighthouse-blog.sigmaprime.io
[Documentation](https://lighthouse-book.sigmaprime.io) [Documentation](https://lighthouse-book.sigmaprime.io)

View File

@ -933,7 +933,7 @@ impl<T: BeaconChainTypes> GossipVerifiedBlock<T> {
// We check this *before* we load the parent so that we can return a more detailed error. // We check this *before* we load the parent so that we can return a more detailed error.
let block = check_block_is_finalized_checkpoint_or_descendant( let block = check_block_is_finalized_checkpoint_or_descendant(
chain, chain,
&chain.canonical_head.fork_choice_write_lock(), &chain.canonical_head.fork_choice_read_lock(),
block, block,
)?; )?;

View File

@ -751,7 +751,7 @@ where
runtime_context runtime_context
.executor .executor
.spawn_without_exit(async move { server.await }, "http-metrics"); .spawn_without_exit(server, "http-metrics");
Some(listen_addr) Some(listen_addr)
} else { } else {

View File

@ -12,12 +12,13 @@ use types::{
}; };
impl<T: EthSpec> ExecutionLayer<T> { impl<T: EthSpec> ExecutionLayer<T> {
/// Verify `payload.block_hash` locally within Lighthouse. /// Calculate the block hash of an execution block.
/// ///
/// No remote calls to the execution client will be made, so this is quite a cheap check. /// Return `(block_hash, transactions_root)`, where `transactions_root` is the root of the RLP
pub fn verify_payload_block_hash(&self, payload: ExecutionPayloadRef<T>) -> Result<(), Error> { /// transactions.
let _timer = metrics::start_timer(&metrics::EXECUTION_LAYER_VERIFY_BLOCK_HASH); pub fn calculate_execution_block_hash(
payload: ExecutionPayloadRef<T>,
) -> (ExecutionBlockHash, Hash256) {
// Calculate the transactions root. // Calculate the transactions root.
// We're currently using a deprecated Parity library for this. We should move to a // We're currently using a deprecated Parity library for this. We should move to a
// better alternative when one appears, possibly following Reth. // better alternative when one appears, possibly following Reth.
@ -49,7 +50,19 @@ impl<T: EthSpec> ExecutionLayer<T> {
// Hash the RLP encoding of the block header. // Hash the RLP encoding of the block header.
let rlp_block_header = rlp_encode_block_header(&exec_block_header); let rlp_block_header = rlp_encode_block_header(&exec_block_header);
let header_hash = ExecutionBlockHash::from_root(keccak256(&rlp_block_header)); (
ExecutionBlockHash::from_root(keccak256(&rlp_block_header)),
rlp_transactions_root,
)
}
/// Verify `payload.block_hash` locally within Lighthouse.
///
/// No remote calls to the execution client will be made, so this is quite a cheap check.
pub fn verify_payload_block_hash(&self, payload: ExecutionPayloadRef<T>) -> Result<(), Error> {
let _timer = metrics::start_timer(&metrics::EXECUTION_LAYER_VERIFY_BLOCK_HASH);
let (header_hash, rlp_transactions_root) = Self::calculate_execution_block_hash(payload);
if header_hash != payload.block_hash() { if header_hash != payload.block_hash() {
return Err(Error::BlockHashMismatch { return Err(Error::BlockHashMismatch {

View File

@ -465,7 +465,7 @@ impl<T: EthSpec> ExecutionLayer<T> {
/// Attempt to retrieve a full payload from the payload cache by the payload root /// Attempt to retrieve a full payload from the payload cache by the payload root
pub fn get_payload_by_root(&self, root: &Hash256) -> Option<ExecutionPayload<T>> { pub fn get_payload_by_root(&self, root: &Hash256) -> Option<ExecutionPayload<T>> {
self.inner.payload_cache.pop(root) self.inner.payload_cache.get(root)
} }
pub fn executor(&self) -> &TaskExecutor { pub fn executor(&self) -> &TaskExecutor {

View File

@ -30,4 +30,8 @@ impl<T: EthSpec> PayloadCache<T> {
pub fn pop(&self, root: &Hash256) -> Option<ExecutionPayload<T>> { pub fn pop(&self, root: &Hash256) -> Option<ExecutionPayload<T>> {
self.payloads.lock().pop(&PayloadCacheId(*root)) self.payloads.lock().pop(&PayloadCacheId(*root))
} }
pub fn get(&self, hash: &Hash256) -> Option<ExecutionPayload<T>> {
self.payloads.lock().get(&PayloadCacheId(*hash)).cloned()
}
} }

View File

@ -38,7 +38,7 @@ async fn returns_200_ok() {
}; };
let (listening_socket, server) = http_metrics::serve(ctx, server_shutdown).unwrap(); let (listening_socket, server) = http_metrics::serve(ctx, server_shutdown).unwrap();
tokio::spawn(async { server.await }); tokio::spawn(server);
let url = format!( let url = format!(
"http://{}:{}/metrics", "http://{}:{}/metrics",

View File

@ -1,5 +1,5 @@
use crate::listen_addr::{ListenAddr, ListenAddress}; use crate::listen_addr::{ListenAddr, ListenAddress};
use crate::rpc::config::OutboundRateLimiterConfig; use crate::rpc::config::{InboundRateLimiterConfig, OutboundRateLimiterConfig};
use crate::types::GossipKind; use crate::types::GossipKind;
use crate::{Enr, PeerIdSerialized}; use crate::{Enr, PeerIdSerialized};
use directory::{ use directory::{
@ -148,6 +148,9 @@ pub struct Config {
/// Configures if/where invalid blocks should be stored. /// Configures if/where invalid blocks should be stored.
pub invalid_block_storage: Option<PathBuf>, pub invalid_block_storage: Option<PathBuf>,
/// Configuration for the inbound rate limiter (requests received by this node).
pub inbound_rate_limiter_config: Option<InboundRateLimiterConfig>,
} }
impl Config { impl Config {
@ -333,6 +336,7 @@ impl Default for Config {
enable_light_client_server: false, enable_light_client_server: false,
outbound_rate_limiter_config: None, outbound_rate_limiter_config: None,
invalid_block_storage: None, invalid_block_storage: None,
inbound_rate_limiter_config: None,
} }
} }
} }

View File

@ -1,4 +1,4 @@
///! The subnet predicate used for searching for a particular subnet. //! The subnet predicate used for searching for a particular subnet.
use super::*; use super::*;
use crate::types::{EnrAttestationBitfield, EnrSyncCommitteeBitfield}; use crate::types::{EnrAttestationBitfield, EnrSyncCommitteeBitfield};
use slog::trace; use slog::trace;

View File

@ -1278,7 +1278,7 @@ impl<TSpec: EthSpec> PeerManager<TSpec> {
); );
} }
let mut score_peers: &mut (f64, usize) = avg_score_per_client let score_peers: &mut (f64, usize) = avg_score_per_client
.entry(peer_info.client().kind.to_string()) .entry(peer_info.client().kind.to_string())
.or_default(); .or_default();
score_peers.0 += peer_info.score().score(); score_peers.0 += peer_info.score().score();

View File

@ -58,9 +58,31 @@ impl FromStr for ProtocolQuota {
} }
} }
/// Configurations for the rate limiter applied to outbound requests (made by the node itself). #[derive(Clone, Serialize, Deserialize, PartialEq, Eq, Debug, Default)]
pub struct OutboundRateLimiterConfig(pub RateLimiterConfig);
#[derive(Clone, Serialize, Deserialize, PartialEq, Eq, Debug, Default)]
pub struct InboundRateLimiterConfig(pub RateLimiterConfig);
impl FromStr for OutboundRateLimiterConfig {
type Err = &'static str;
fn from_str(s: &str) -> Result<Self, Self::Err> {
RateLimiterConfig::from_str(s).map(Self)
}
}
impl FromStr for InboundRateLimiterConfig {
type Err = &'static str;
fn from_str(s: &str) -> Result<Self, Self::Err> {
RateLimiterConfig::from_str(s).map(Self)
}
}
/// Configurations for the rate limiter.
#[derive(Clone, Serialize, Deserialize, PartialEq, Eq)] #[derive(Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct OutboundRateLimiterConfig { pub struct RateLimiterConfig {
pub(super) ping_quota: Quota, pub(super) ping_quota: Quota,
pub(super) meta_data_quota: Quota, pub(super) meta_data_quota: Quota,
pub(super) status_quota: Quota, pub(super) status_quota: Quota,
@ -69,9 +91,10 @@ pub struct OutboundRateLimiterConfig {
pub(super) blocks_by_root_quota: Quota, pub(super) blocks_by_root_quota: Quota,
pub(super) blobs_by_range_quota: Quota, pub(super) blobs_by_range_quota: Quota,
pub(super) blobs_by_root_quota: Quota, pub(super) blobs_by_root_quota: Quota,
pub(super) light_client_bootstrap_quota: Quota,
} }
impl OutboundRateLimiterConfig { impl RateLimiterConfig {
pub const DEFAULT_PING_QUOTA: Quota = Quota::n_every(2, 10); pub const DEFAULT_PING_QUOTA: Quota = Quota::n_every(2, 10);
pub const DEFAULT_META_DATA_QUOTA: Quota = Quota::n_every(2, 5); pub const DEFAULT_META_DATA_QUOTA: Quota = Quota::n_every(2, 5);
pub const DEFAULT_STATUS_QUOTA: Quota = Quota::n_every(5, 15); pub const DEFAULT_STATUS_QUOTA: Quota = Quota::n_every(5, 15);
@ -82,11 +105,12 @@ impl OutboundRateLimiterConfig {
pub const DEFAULT_BLOBS_BY_RANGE_QUOTA: Quota = pub const DEFAULT_BLOBS_BY_RANGE_QUOTA: Quota =
Quota::n_every(methods::MAX_REQUEST_BLOB_SIDECARS, 10); Quota::n_every(methods::MAX_REQUEST_BLOB_SIDECARS, 10);
pub const DEFAULT_BLOBS_BY_ROOT_QUOTA: Quota = Quota::n_every(128, 10); pub const DEFAULT_BLOBS_BY_ROOT_QUOTA: Quota = Quota::n_every(128, 10);
pub const DEFAULT_LIGHT_CLIENT_BOOTSTRAP_QUOTA: Quota = Quota::one_every(10);
} }
impl Default for OutboundRateLimiterConfig { impl Default for RateLimiterConfig {
fn default() -> Self { fn default() -> Self {
OutboundRateLimiterConfig { RateLimiterConfig {
ping_quota: Self::DEFAULT_PING_QUOTA, ping_quota: Self::DEFAULT_PING_QUOTA,
meta_data_quota: Self::DEFAULT_META_DATA_QUOTA, meta_data_quota: Self::DEFAULT_META_DATA_QUOTA,
status_quota: Self::DEFAULT_STATUS_QUOTA, status_quota: Self::DEFAULT_STATUS_QUOTA,
@ -95,11 +119,12 @@ impl Default for OutboundRateLimiterConfig {
blocks_by_root_quota: Self::DEFAULT_BLOCKS_BY_ROOT_QUOTA, blocks_by_root_quota: Self::DEFAULT_BLOCKS_BY_ROOT_QUOTA,
blobs_by_range_quota: Self::DEFAULT_BLOBS_BY_RANGE_QUOTA, blobs_by_range_quota: Self::DEFAULT_BLOBS_BY_RANGE_QUOTA,
blobs_by_root_quota: Self::DEFAULT_BLOBS_BY_ROOT_QUOTA, blobs_by_root_quota: Self::DEFAULT_BLOBS_BY_ROOT_QUOTA,
light_client_bootstrap_quota: Self::DEFAULT_LIGHT_CLIENT_BOOTSTRAP_QUOTA,
} }
} }
} }
impl Debug for OutboundRateLimiterConfig { impl Debug for RateLimiterConfig {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
macro_rules! fmt_q { macro_rules! fmt_q {
($quota:expr) => { ($quota:expr) => {
@ -111,7 +136,7 @@ impl Debug for OutboundRateLimiterConfig {
}; };
} }
f.debug_struct("OutboundRateLimiterConfig") f.debug_struct("RateLimiterConfig")
.field("ping", fmt_q!(&self.ping_quota)) .field("ping", fmt_q!(&self.ping_quota))
.field("metadata", fmt_q!(&self.meta_data_quota)) .field("metadata", fmt_q!(&self.meta_data_quota))
.field("status", fmt_q!(&self.status_quota)) .field("status", fmt_q!(&self.status_quota))
@ -128,7 +153,7 @@ impl Debug for OutboundRateLimiterConfig {
/// the default values. Protocol specified more than once use only the first given Quota. /// the default values. Protocol specified more than once use only the first given Quota.
/// ///
/// The expected format is a ';' separated list of [`ProtocolQuota`]. /// The expected format is a ';' separated list of [`ProtocolQuota`].
impl FromStr for OutboundRateLimiterConfig { impl FromStr for RateLimiterConfig {
type Err = &'static str; type Err = &'static str;
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
@ -140,6 +165,8 @@ impl FromStr for OutboundRateLimiterConfig {
let mut blocks_by_root_quota = None; let mut blocks_by_root_quota = None;
let mut blobs_by_range_quota = None; let mut blobs_by_range_quota = None;
let mut blobs_by_root_quota = None; let mut blobs_by_root_quota = None;
let mut light_client_bootstrap_quota = None;
for proto_def in s.split(';') { for proto_def in s.split(';') {
let ProtocolQuota { protocol, quota } = proto_def.parse()?; let ProtocolQuota { protocol, quota } = proto_def.parse()?;
let quota = Some(quota); let quota = Some(quota);
@ -152,10 +179,12 @@ impl FromStr for OutboundRateLimiterConfig {
Protocol::BlobsByRoot => blobs_by_root_quota = blobs_by_root_quota.or(quota), Protocol::BlobsByRoot => blobs_by_root_quota = blobs_by_root_quota.or(quota),
Protocol::Ping => ping_quota = ping_quota.or(quota), Protocol::Ping => ping_quota = ping_quota.or(quota),
Protocol::MetaData => meta_data_quota = meta_data_quota.or(quota), Protocol::MetaData => meta_data_quota = meta_data_quota.or(quota),
Protocol::LightClientBootstrap => return Err("Lighthouse does not send LightClientBootstrap requests. Quota should not be set."), Protocol::LightClientBootstrap => {
light_client_bootstrap_quota = light_client_bootstrap_quota.or(quota)
} }
} }
Ok(OutboundRateLimiterConfig { }
Ok(RateLimiterConfig {
ping_quota: ping_quota.unwrap_or(Self::DEFAULT_PING_QUOTA), ping_quota: ping_quota.unwrap_or(Self::DEFAULT_PING_QUOTA),
meta_data_quota: meta_data_quota.unwrap_or(Self::DEFAULT_META_DATA_QUOTA), meta_data_quota: meta_data_quota.unwrap_or(Self::DEFAULT_META_DATA_QUOTA),
status_quota: status_quota.unwrap_or(Self::DEFAULT_STATUS_QUOTA), status_quota: status_quota.unwrap_or(Self::DEFAULT_STATUS_QUOTA),
@ -167,6 +196,8 @@ impl FromStr for OutboundRateLimiterConfig {
blobs_by_range_quota: blobs_by_range_quota blobs_by_range_quota: blobs_by_range_quota
.unwrap_or(Self::DEFAULT_BLOBS_BY_RANGE_QUOTA), .unwrap_or(Self::DEFAULT_BLOBS_BY_RANGE_QUOTA),
blobs_by_root_quota: blobs_by_root_quota.unwrap_or(Self::DEFAULT_BLOBS_BY_ROOT_QUOTA), blobs_by_root_quota: blobs_by_root_quota.unwrap_or(Self::DEFAULT_BLOBS_BY_ROOT_QUOTA),
light_client_bootstrap_quota: light_client_bootstrap_quota
.unwrap_or(Self::DEFAULT_LIGHT_CLIENT_BOOTSTRAP_QUOTA),
}) })
} }
} }

View File

@ -17,7 +17,6 @@ use slog::{crit, debug, o};
use std::marker::PhantomData; use std::marker::PhantomData;
use std::sync::Arc; use std::sync::Arc;
use std::task::{Context, Poll}; use std::task::{Context, Poll};
use std::time::Duration;
use types::{EthSpec, ForkContext}; use types::{EthSpec, ForkContext};
pub(crate) use handler::HandlerErr; pub(crate) use handler::HandlerErr;
@ -33,7 +32,7 @@ pub use methods::{
pub(crate) use outbound::OutboundRequest; pub(crate) use outbound::OutboundRequest;
pub use protocol::{max_rpc_size, Protocol, RPCError}; pub use protocol::{max_rpc_size, Protocol, RPCError};
use self::config::OutboundRateLimiterConfig; use self::config::{InboundRateLimiterConfig, OutboundRateLimiterConfig};
use self::self_limiter::SelfRateLimiter; use self::self_limiter::SelfRateLimiter;
pub(crate) mod codec; pub(crate) mod codec;
@ -113,7 +112,7 @@ type BehaviourAction<Id, TSpec> =
/// logic. /// logic.
pub struct RPC<Id: ReqId, TSpec: EthSpec> { pub struct RPC<Id: ReqId, TSpec: EthSpec> {
/// Rate limiter /// Rate limiter
limiter: RateLimiter, limiter: Option<RateLimiter>,
/// Rate limiter for our own requests. /// Rate limiter for our own requests.
self_limiter: Option<SelfRateLimiter<Id, TSpec>>, self_limiter: Option<SelfRateLimiter<Id, TSpec>>,
/// Queue of events to be processed. /// Queue of events to be processed.
@ -128,38 +127,24 @@ impl<Id: ReqId, TSpec: EthSpec> RPC<Id, TSpec> {
pub fn new( pub fn new(
fork_context: Arc<ForkContext>, fork_context: Arc<ForkContext>,
enable_light_client_server: bool, enable_light_client_server: bool,
inbound_rate_limiter_config: Option<InboundRateLimiterConfig>,
outbound_rate_limiter_config: Option<OutboundRateLimiterConfig>, outbound_rate_limiter_config: Option<OutboundRateLimiterConfig>,
log: slog::Logger, log: slog::Logger,
) -> Self { ) -> Self {
let log = log.new(o!("service" => "libp2p_rpc")); let log = log.new(o!("service" => "libp2p_rpc"));
let limiter = RateLimiter::builder() let inbound_limiter = inbound_rate_limiter_config.map(|config| {
.n_every(Protocol::MetaData, 2, Duration::from_secs(5)) debug!(log, "Using inbound rate limiting params"; "config" => ?config);
.n_every(Protocol::Ping, 2, Duration::from_secs(10)) RateLimiter::new_with_config(config.0)
.n_every(Protocol::Status, 5, Duration::from_secs(15)) .expect("Inbound limiter configuration parameters are valid")
.one_every(Protocol::Goodbye, Duration::from_secs(10)) });
.one_every(Protocol::LightClientBootstrap, Duration::from_secs(10))
.n_every(
Protocol::BlocksByRange,
methods::MAX_REQUEST_BLOCKS,
Duration::from_secs(10),
)
.n_every(Protocol::BlocksByRoot, 128, Duration::from_secs(10))
.n_every(Protocol::BlobsByRoot, 128, Duration::from_secs(10))
.n_every(
Protocol::BlobsByRange,
MAX_REQUEST_BLOB_SIDECARS,
Duration::from_secs(10),
)
.build()
.expect("Configuration parameters are valid");
let self_limiter = outbound_rate_limiter_config.map(|config| { let self_limiter = outbound_rate_limiter_config.map(|config| {
SelfRateLimiter::new(config, log.clone()).expect("Configuration parameters are valid") SelfRateLimiter::new(config, log.clone()).expect("Configuration parameters are valid")
}); });
RPC { RPC {
limiter, limiter: inbound_limiter,
self_limiter, self_limiter,
events: Vec::new(), events: Vec::new(),
fork_context, fork_context,
@ -249,8 +234,9 @@ where
event: <Self::ConnectionHandler as ConnectionHandler>::OutEvent, event: <Self::ConnectionHandler as ConnectionHandler>::OutEvent,
) { ) {
if let Ok(RPCReceived::Request(ref id, ref req)) = event { if let Ok(RPCReceived::Request(ref id, ref req)) = event {
if let Some(limiter) = self.limiter.as_mut() {
// check if the request is conformant to the quota // check if the request is conformant to the quota
match self.limiter.allows(&peer_id, req) { match limiter.allows(&peer_id, req) {
Ok(()) => { Ok(()) => {
// send the event to the user // send the event to the user
self.events self.events
@ -294,6 +280,15 @@ where
); );
} }
} }
} else {
// No rate limiting, send the event to the user
self.events
.push(NetworkBehaviourAction::GenerateEvent(RPCMessage {
peer_id,
conn_id,
event,
}))
}
} else { } else {
self.events self.events
.push(NetworkBehaviourAction::GenerateEvent(RPCMessage { .push(NetworkBehaviourAction::GenerateEvent(RPCMessage {
@ -310,7 +305,9 @@ where
_: &mut impl PollParameters, _: &mut impl PollParameters,
) -> Poll<NetworkBehaviourAction<Self::OutEvent, Self::ConnectionHandler>> { ) -> Poll<NetworkBehaviourAction<Self::OutEvent, Self::ConnectionHandler>> {
// let the rate limiter prune. // let the rate limiter prune.
let _ = self.limiter.poll_unpin(cx); if let Some(limiter) = self.limiter.as_mut() {
let _ = limiter.poll_unpin(cx);
}
if let Some(self_limiter) = self.self_limiter.as_mut() { if let Some(self_limiter) = self.self_limiter.as_mut() {
if let Poll::Ready(event) = self_limiter.poll_ready(cx) { if let Poll::Ready(event) = self_limiter.poll_ready(cx) {

View File

@ -1,3 +1,4 @@
use super::config::RateLimiterConfig;
use crate::rpc::Protocol; use crate::rpc::Protocol;
use fnv::FnvHashMap; use fnv::FnvHashMap;
use libp2p::PeerId; use libp2p::PeerId;
@ -151,29 +152,6 @@ impl RPCRateLimiterBuilder {
self self
} }
/// Allow one token every `time_period` to be used for this `protocol`.
/// This produces a hard limit.
pub fn one_every(self, protocol: Protocol, time_period: Duration) -> Self {
self.set_quota(
protocol,
Quota {
replenish_all_every: time_period,
max_tokens: 1,
},
)
}
/// Allow `n` tokens to be use used every `time_period` for this `protocol`.
pub fn n_every(self, protocol: Protocol, n: u64, time_period: Duration) -> Self {
self.set_quota(
protocol,
Quota {
max_tokens: n,
replenish_all_every: time_period,
},
)
}
pub fn build(self) -> Result<RPCRateLimiter, &'static str> { pub fn build(self) -> Result<RPCRateLimiter, &'static str> {
// get our quotas // get our quotas
let ping_quota = self.ping_quota.ok_or("Ping quota not specified")?; let ping_quota = self.ping_quota.ok_or("Ping quota not specified")?;
@ -254,6 +232,29 @@ impl<T: EthSpec> RateLimiterItem for super::OutboundRequest<T> {
} }
} }
impl RPCRateLimiter { impl RPCRateLimiter {
pub fn new_with_config(config: RateLimiterConfig) -> Result<Self, &'static str> {
// Destructure to make sure every configuration value is used.
let RateLimiterConfig {
ping_quota,
meta_data_quota,
status_quota,
goodbye_quota,
blocks_by_range_quota,
blocks_by_root_quota,
light_client_bootstrap_quota,
} = config;
Self::builder()
.set_quota(Protocol::Ping, ping_quota)
.set_quota(Protocol::MetaData, meta_data_quota)
.set_quota(Protocol::Status, status_quota)
.set_quota(Protocol::Goodbye, goodbye_quota)
.set_quota(Protocol::BlocksByRange, blocks_by_range_quota)
.set_quota(Protocol::BlocksByRoot, blocks_by_root_quota)
.set_quota(Protocol::LightClientBootstrap, light_client_bootstrap_quota)
.build()
}
/// Get a builder instance. /// Get a builder instance.
pub fn builder() -> RPCRateLimiterBuilder { pub fn builder() -> RPCRateLimiterBuilder {
RPCRateLimiterBuilder::default() RPCRateLimiterBuilder::default()

View File

@ -52,32 +52,7 @@ impl<Id: ReqId, TSpec: EthSpec> SelfRateLimiter<Id, TSpec> {
/// Creates a new [`SelfRateLimiter`] based on configration values. /// Creates a new [`SelfRateLimiter`] based on configration values.
pub fn new(config: OutboundRateLimiterConfig, log: Logger) -> Result<Self, &'static str> { pub fn new(config: OutboundRateLimiterConfig, log: Logger) -> Result<Self, &'static str> {
debug!(log, "Using self rate limiting params"; "config" => ?config); debug!(log, "Using self rate limiting params"; "config" => ?config);
// Destructure to make sure every configuration value is used. let limiter = RateLimiter::new_with_config(config.0)?;
let OutboundRateLimiterConfig {
ping_quota,
meta_data_quota,
status_quota,
goodbye_quota,
blocks_by_range_quota,
blocks_by_root_quota,
blobs_by_range_quota,
blobs_by_root_quota,
} = config;
let limiter = RateLimiter::builder()
.set_quota(Protocol::Ping, ping_quota)
.set_quota(Protocol::MetaData, meta_data_quota)
.set_quota(Protocol::Status, status_quota)
.set_quota(Protocol::Goodbye, goodbye_quota)
.set_quota(Protocol::BlocksByRange, blocks_by_range_quota)
.set_quota(Protocol::BlocksByRoot, blocks_by_root_quota)
.set_quota(Protocol::BlobsByRange, blobs_by_range_quota)
.set_quota(Protocol::BlobsByRoot, blobs_by_root_quota)
// Manually set the LightClientBootstrap quota, since we use the same rate limiter for
// inbound and outbound requests, and the LightClientBootstrap is an only inbound
// protocol.
.one_every(Protocol::LightClientBootstrap, Duration::from_secs(10))
.build()?;
Ok(SelfRateLimiter { Ok(SelfRateLimiter {
delayed_requests: Default::default(), delayed_requests: Default::default(),

View File

@ -269,6 +269,7 @@ impl<AppReqId: ReqId, TSpec: EthSpec> Network<AppReqId, TSpec> {
let eth2_rpc = RPC::new( let eth2_rpc = RPC::new(
ctx.fork_context.clone(), ctx.fork_context.clone(),
config.enable_light_client_server, config.enable_light_client_server,
config.inbound_rate_limiter_config.clone(),
config.outbound_rate_limiter_config.clone(), config.outbound_rate_limiter_config.clone(),
log.clone(), log.clone(),
); );

View File

@ -47,7 +47,3 @@ delay_map = "0.3.0"
ethereum-types = { version = "0.14.1", optional = true } ethereum-types = { version = "0.14.1", optional = true }
operation_pool = { path = "../operation_pool" } operation_pool = { path = "../operation_pool" }
execution_layer = { path = "../execution_layer" } execution_layer = { path = "../execution_layer" }
[features]
deterministic_long_lived_attnets = [ "ethereum-types" ]
# default = ["deterministic_long_lived_attnets"]

View File

@ -317,8 +317,7 @@ impl<T: BeaconChainTypes> NetworkService<T> {
// attestation subnet service // attestation subnet service
let attestation_service = AttestationService::new( let attestation_service = AttestationService::new(
beacon_chain.clone(), beacon_chain.clone(),
#[cfg(feature = "deterministic_long_lived_attnets")] network_globals.local_enr().node_id(),
network_globals.local_enr().node_id().raw().into(),
config, config,
&network_log, &network_log,
); );

View File

@ -3,7 +3,6 @@
//! determines whether attestations should be aggregated and/or passed to the beacon node. //! determines whether attestations should be aggregated and/or passed to the beacon node.
use super::SubnetServiceMessage; use super::SubnetServiceMessage;
#[cfg(any(test, feature = "deterministic_long_lived_attnets"))]
use std::collections::HashSet; use std::collections::HashSet;
use std::collections::{HashMap, VecDeque}; use std::collections::{HashMap, VecDeque};
use std::pin::Pin; use std::pin::Pin;
@ -14,10 +13,8 @@ use std::time::Duration;
use beacon_chain::{BeaconChain, BeaconChainTypes}; use beacon_chain::{BeaconChain, BeaconChainTypes};
use delay_map::{HashMapDelay, HashSetDelay}; use delay_map::{HashMapDelay, HashSetDelay};
use futures::prelude::*; use futures::prelude::*;
use lighthouse_network::{NetworkConfig, Subnet, SubnetDiscovery}; use lighthouse_network::{discv5::enr::NodeId, NetworkConfig, Subnet, SubnetDiscovery};
#[cfg(not(feature = "deterministic_long_lived_attnets"))] use slog::{debug, error, info, o, trace, warn};
use rand::seq::SliceRandom;
use slog::{debug, error, o, trace, warn};
use slot_clock::SlotClock; use slot_clock::SlotClock;
use types::{Attestation, EthSpec, Slot, SubnetId, ValidatorSubscription}; use types::{Attestation, EthSpec, Slot, SubnetId, ValidatorSubscription};
@ -27,10 +24,6 @@ use crate::metrics;
/// slot is less than this number, skip the peer discovery process. /// slot is less than this number, skip the peer discovery process.
/// Subnet discovery query takes at most 30 secs, 2 slots take 24s. /// Subnet discovery query takes at most 30 secs, 2 slots take 24s.
pub(crate) const MIN_PEER_DISCOVERY_SLOT_LOOK_AHEAD: u64 = 2; pub(crate) const MIN_PEER_DISCOVERY_SLOT_LOOK_AHEAD: u64 = 2;
/// The time (in slots) before a last seen validator is considered absent and we unsubscribe from
/// the random gossip topics that we subscribed to due to the validator connection.
#[cfg(not(feature = "deterministic_long_lived_attnets"))]
const LAST_SEEN_VALIDATOR_TIMEOUT_SLOTS: u32 = 150;
/// The fraction of a slot that we subscribe to a subnet before the required slot. /// The fraction of a slot that we subscribe to a subnet before the required slot.
/// ///
/// Currently a whole slot ahead. /// Currently a whole slot ahead.
@ -67,30 +60,23 @@ pub struct AttestationService<T: BeaconChainTypes> {
/// Subnets we are currently subscribed to as short lived subscriptions. /// Subnets we are currently subscribed to as short lived subscriptions.
/// ///
/// Once they expire, we unsubscribe from these. /// Once they expire, we unsubscribe from these.
/// We subscribe to subnets when we are an aggregator for an exact subnet.
short_lived_subscriptions: HashMapDelay<SubnetId, Slot>, short_lived_subscriptions: HashMapDelay<SubnetId, Slot>,
/// Subnets we are currently subscribed to as long lived subscriptions. /// Subnets we are currently subscribed to as long lived subscriptions.
/// ///
/// We advertise these in our ENR. When these expire, the subnet is removed from our ENR. /// We advertise these in our ENR. When these expire, the subnet is removed from our ENR.
#[cfg(feature = "deterministic_long_lived_attnets")] /// These are required of all beacon nodes. The exact number is determined by the chain
/// specification.
long_lived_subscriptions: HashSet<SubnetId>, long_lived_subscriptions: HashSet<SubnetId>,
#[cfg(not(feature = "deterministic_long_lived_attnets"))]
long_lived_subscriptions: HashMapDelay<SubnetId, Slot>,
/// Short lived subscriptions that need to be done in the future. /// Short lived subscriptions that need to be executed in the future.
scheduled_short_lived_subscriptions: HashSetDelay<ExactSubnet>, scheduled_short_lived_subscriptions: HashSetDelay<ExactSubnet>,
/// A collection timeouts to track the existence of aggregate validator subscriptions at an /// A collection timeouts to track the existence of aggregate validator subscriptions at an
/// `ExactSubnet`. /// `ExactSubnet`.
aggregate_validators_on_subnet: Option<HashSetDelay<ExactSubnet>>, aggregate_validators_on_subnet: Option<HashSetDelay<ExactSubnet>>,
/// A collection of seen validators. These dictate how many random subnets we should be
/// subscribed to. As these time out, we unsubscribe for the required random subnets and update
/// our ENR.
/// This is a set of validator indices.
#[cfg(not(feature = "deterministic_long_lived_attnets"))]
known_validators: HashSetDelay<u64>,
/// The waker for the current thread. /// The waker for the current thread.
waker: Option<std::task::Waker>, waker: Option<std::task::Waker>,
@ -100,16 +86,10 @@ pub struct AttestationService<T: BeaconChainTypes> {
/// We are always subscribed to all subnets. /// We are always subscribed to all subnets.
subscribe_all_subnets: bool, subscribe_all_subnets: bool,
/// For how many slots we subscribe to long lived subnets.
#[cfg(not(feature = "deterministic_long_lived_attnets"))]
long_lived_subnet_subscription_slots: u64,
/// Our Discv5 node_id. /// Our Discv5 node_id.
#[cfg(feature = "deterministic_long_lived_attnets")] node_id: NodeId,
node_id: ethereum_types::U256,
/// Future used to manage subscribing and unsubscribing from long lived subnets. /// Future used to manage subscribing and unsubscribing from long lived subnets.
#[cfg(feature = "deterministic_long_lived_attnets")]
next_long_lived_subscription_event: Pin<Box<tokio::time::Sleep>>, next_long_lived_subscription_event: Pin<Box<tokio::time::Sleep>>,
/// Whether this node is a block proposer-only node. /// Whether this node is a block proposer-only node.
@ -122,62 +102,22 @@ pub struct AttestationService<T: BeaconChainTypes> {
impl<T: BeaconChainTypes> AttestationService<T> { impl<T: BeaconChainTypes> AttestationService<T> {
/* Public functions */ /* Public functions */
#[cfg(not(feature = "deterministic_long_lived_attnets"))] /// Establish the service based on the passed configuration.
pub fn new( pub fn new(
beacon_chain: Arc<BeaconChain<T>>, beacon_chain: Arc<BeaconChain<T>>,
node_id: NodeId,
config: &NetworkConfig, config: &NetworkConfig,
log: &slog::Logger, log: &slog::Logger,
) -> Self { ) -> Self {
let log = log.new(o!("service" => "attestation_service")); let log = log.new(o!("service" => "attestation_service"));
// Calculate the random subnet duration from the spec constants.
let spec = &beacon_chain.spec;
let slot_duration = beacon_chain.slot_clock.slot_duration();
let long_lived_subnet_subscription_slots = spec
.epochs_per_random_subnet_subscription
.saturating_mul(T::EthSpec::slots_per_epoch());
let long_lived_subscription_duration = Duration::from_millis(
slot_duration.as_millis() as u64 * long_lived_subnet_subscription_slots,
);
// Panics on overflow. Ensure LAST_SEEN_VALIDATOR_TIMEOUT_SLOTS is not too large.
let last_seen_val_timeout = slot_duration
.checked_mul(LAST_SEEN_VALIDATOR_TIMEOUT_SLOTS)
.expect("LAST_SEEN_VALIDATOR_TIMEOUT must not be ridiculously large");
let track_validators = !config.import_all_attestations;
let aggregate_validators_on_subnet =
track_validators.then(|| HashSetDelay::new(slot_duration));
AttestationService {
events: VecDeque::with_capacity(10),
beacon_chain,
short_lived_subscriptions: HashMapDelay::new(slot_duration),
long_lived_subscriptions: HashMapDelay::new(long_lived_subscription_duration),
scheduled_short_lived_subscriptions: HashSetDelay::default(),
aggregate_validators_on_subnet,
known_validators: HashSetDelay::new(last_seen_val_timeout),
waker: None,
discovery_disabled: config.disable_discovery,
proposer_only: config.proposer_only,
subscribe_all_subnets: config.subscribe_all_subnets,
long_lived_subnet_subscription_slots,
log,
}
}
#[cfg(feature = "deterministic_long_lived_attnets")]
pub fn new(
beacon_chain: Arc<BeaconChain<T>>,
node_id: ethereum_types::U256,
config: &NetworkConfig,
log: &slog::Logger,
) -> Self {
let log = log.new(o!("service" => "attestation_service"));
// Calculate the random subnet duration from the spec constants.
let slot_duration = beacon_chain.slot_clock.slot_duration(); let slot_duration = beacon_chain.slot_clock.slot_duration();
slog::info!(log, "Deterministic long lived subnets enabled"; "subnets_per_node" => beacon_chain.spec.subnets_per_node); if config.subscribe_all_subnets {
slog::info!(log, "Subscribing to all subnets");
} else {
slog::info!(log, "Deterministic long lived subnets enabled"; "subnets_per_node" => beacon_chain.spec.subnets_per_node, "subscription_duration_in_epochs" => beacon_chain.spec.epochs_per_subnet_subscription);
}
let track_validators = !config.import_all_attestations; let track_validators = !config.import_all_attestations;
let aggregate_validators_on_subnet = let aggregate_validators_on_subnet =
@ -198,9 +138,15 @@ impl<T: BeaconChainTypes> AttestationService<T> {
// value with a smarter timing // value with a smarter timing
Box::pin(tokio::time::sleep(Duration::from_secs(1))) Box::pin(tokio::time::sleep(Duration::from_secs(1)))
}, },
proposer_only: config.proposer_only,
log, log,
}; };
// If we are not subscribed to all subnets, handle the deterministic set of subnets
if !config.subscribe_all_subnets {
service.recompute_long_lived_subnets(); service.recompute_long_lived_subnets();
}
service service
} }
@ -210,20 +156,12 @@ impl<T: BeaconChainTypes> AttestationService<T> {
if self.subscribe_all_subnets { if self.subscribe_all_subnets {
self.beacon_chain.spec.attestation_subnet_count as usize self.beacon_chain.spec.attestation_subnet_count as usize
} else { } else {
#[cfg(feature = "deterministic_long_lived_attnets")]
let count = self let count = self
.short_lived_subscriptions .short_lived_subscriptions
.keys() .keys()
.chain(self.long_lived_subscriptions.iter()) .chain(self.long_lived_subscriptions.iter())
.collect::<HashSet<_>>() .collect::<HashSet<_>>()
.len(); .len();
#[cfg(not(feature = "deterministic_long_lived_attnets"))]
let count = self
.short_lived_subscriptions
.keys()
.chain(self.long_lived_subscriptions.keys())
.collect::<HashSet<_>>()
.len();
count count
} }
} }
@ -236,20 +174,20 @@ impl<T: BeaconChainTypes> AttestationService<T> {
subscription_kind: SubscriptionKind, subscription_kind: SubscriptionKind,
) -> bool { ) -> bool {
match subscription_kind { match subscription_kind {
#[cfg(feature = "deterministic_long_lived_attnets")]
SubscriptionKind::LongLived => self.long_lived_subscriptions.contains(subnet_id), SubscriptionKind::LongLived => self.long_lived_subscriptions.contains(subnet_id),
#[cfg(not(feature = "deterministic_long_lived_attnets"))]
SubscriptionKind::LongLived => self.long_lived_subscriptions.contains_key(subnet_id),
SubscriptionKind::ShortLived => self.short_lived_subscriptions.contains_key(subnet_id), SubscriptionKind::ShortLived => self.short_lived_subscriptions.contains_key(subnet_id),
} }
} }
#[cfg(test)]
pub(crate) fn long_lived_subscriptions(&self) -> &HashSet<SubnetId> {
&self.long_lived_subscriptions
}
/// Processes a list of validator subscriptions. /// Processes a list of validator subscriptions.
/// ///
/// This will: /// This will:
/// - Register new validators as being known. /// - Register new validators as being known.
/// - Subscribe to the required number of random subnets.
/// - Update the local ENR for new random subnets due to seeing new validators.
/// - Search for peers for required subnets. /// - Search for peers for required subnets.
/// - Request subscriptions for subnets on specific slots when required. /// - Request subscriptions for subnets on specific slots when required.
/// - Build the timeouts for each of these events. /// - Build the timeouts for each of these events.
@ -267,18 +205,17 @@ impl<T: BeaconChainTypes> AttestationService<T> {
// Maps each subnet_id subscription to it's highest slot // Maps each subnet_id subscription to it's highest slot
let mut subnets_to_discover: HashMap<SubnetId, Slot> = HashMap::new(); let mut subnets_to_discover: HashMap<SubnetId, Slot> = HashMap::new();
// Registers the validator with the attestation service.
for subscription in subscriptions { for subscription in subscriptions {
metrics::inc_counter(&metrics::SUBNET_SUBSCRIPTION_REQUESTS); metrics::inc_counter(&metrics::SUBNET_SUBSCRIPTION_REQUESTS);
// Registers the validator with the attestation service.
// This will subscribe to long-lived random subnets if required.
trace!(self.log, trace!(self.log,
"Validator subscription"; "Validator subscription";
"subscription" => ?subscription, "subscription" => ?subscription,
); );
#[cfg(not(feature = "deterministic_long_lived_attnets"))]
self.add_known_validator(subscription.validator_index);
// Compute the subnet that is associated with this subscription
let subnet_id = match SubnetId::compute_subnet::<T::EthSpec>( let subnet_id = match SubnetId::compute_subnet::<T::EthSpec>(
subscription.slot, subscription.slot,
subscription.attestation_committee_index, subscription.attestation_committee_index,
@ -316,7 +253,7 @@ impl<T: BeaconChainTypes> AttestationService<T> {
if subscription.is_aggregator { if subscription.is_aggregator {
metrics::inc_counter(&metrics::SUBNET_SUBSCRIPTION_AGGREGATOR_REQUESTS); metrics::inc_counter(&metrics::SUBNET_SUBSCRIPTION_AGGREGATOR_REQUESTS);
if let Err(e) = self.subscribe_to_subnet(exact_subnet) { if let Err(e) = self.subscribe_to_short_lived_subnet(exact_subnet) {
warn!(self.log, warn!(self.log,
"Subscription to subnet error"; "Subscription to subnet error";
"error" => e, "error" => e,
@ -347,14 +284,13 @@ impl<T: BeaconChainTypes> AttestationService<T> {
Ok(()) Ok(())
} }
#[cfg(feature = "deterministic_long_lived_attnets")]
fn recompute_long_lived_subnets(&mut self) { fn recompute_long_lived_subnets(&mut self) {
// Ensure the next computation is scheduled even if assigning subnets fails. // Ensure the next computation is scheduled even if assigning subnets fails.
let next_subscription_event = self let next_subscription_event = self
.recompute_long_lived_subnets_inner() .recompute_long_lived_subnets_inner()
.unwrap_or_else(|_| self.beacon_chain.slot_clock.slot_duration()); .unwrap_or_else(|_| self.beacon_chain.slot_clock.slot_duration());
debug!(self.log, "Recomputing deterministic long lived attnets"); debug!(self.log, "Recomputing deterministic long lived subnets");
self.next_long_lived_subscription_event = self.next_long_lived_subscription_event =
Box::pin(tokio::time::sleep(next_subscription_event)); Box::pin(tokio::time::sleep(next_subscription_event));
@ -365,14 +301,13 @@ impl<T: BeaconChainTypes> AttestationService<T> {
/// Gets the long lived subnets the node should be subscribed to during the current epoch and /// Gets the long lived subnets the node should be subscribed to during the current epoch and
/// the remaining duration for which they remain valid. /// the remaining duration for which they remain valid.
#[cfg(feature = "deterministic_long_lived_attnets")]
fn recompute_long_lived_subnets_inner(&mut self) -> Result<Duration, ()> { fn recompute_long_lived_subnets_inner(&mut self) -> Result<Duration, ()> {
let current_epoch = self.beacon_chain.epoch().map_err( let current_epoch = self.beacon_chain.epoch().map_err(
|e| error!(self.log, "Failed to get the current epoch from clock"; "err" => ?e), |e| error!(self.log, "Failed to get the current epoch from clock"; "err" => ?e),
)?; )?;
let (subnets, next_subscription_epoch) = SubnetId::compute_subnets_for_epoch::<T::EthSpec>( let (subnets, next_subscription_epoch) = SubnetId::compute_subnets_for_epoch::<T::EthSpec>(
self.node_id, self.node_id.raw().into(),
current_epoch, current_epoch,
&self.beacon_chain.spec, &self.beacon_chain.spec,
) )
@ -396,17 +331,12 @@ impl<T: BeaconChainTypes> AttestationService<T> {
Ok(next_subscription_event) Ok(next_subscription_event)
} }
#[cfg(all(test, feature = "deterministic_long_lived_attnets"))]
pub fn update_long_lived_subnets_testing(&mut self, subnets: HashSet<SubnetId>) {
self.update_long_lived_subnets(subnets)
}
/// Updates the long lived subnets. /// Updates the long lived subnets.
/// ///
/// New subnets are registered as subscribed, removed subnets as unsubscribed and the Enr /// New subnets are registered as subscribed, removed subnets as unsubscribed and the Enr
/// updated accordingly. /// updated accordingly.
#[cfg(feature = "deterministic_long_lived_attnets")]
fn update_long_lived_subnets(&mut self, mut subnets: HashSet<SubnetId>) { fn update_long_lived_subnets(&mut self, mut subnets: HashSet<SubnetId>) {
info!(self.log, "Subscribing to long-lived subnets"; "subnets" => ?subnets.iter().collect::<Vec<_>>());
for subnet in &subnets { for subnet in &subnets {
// Add the events for those subnets that are new as long lived subscriptions. // Add the events for those subnets that are new as long lived subscriptions.
if !self.long_lived_subscriptions.contains(subnet) { if !self.long_lived_subscriptions.contains(subnet) {
@ -430,28 +360,15 @@ impl<T: BeaconChainTypes> AttestationService<T> {
} }
} }
// Check for subnets that are being removed // Update the long_lived_subnets set and check for subnets that are being removed
std::mem::swap(&mut self.long_lived_subscriptions, &mut subnets); std::mem::swap(&mut self.long_lived_subscriptions, &mut subnets);
for subnet in subnets { for subnet in subnets {
if !self.long_lived_subscriptions.contains(&subnet) { if !self.long_lived_subscriptions.contains(&subnet) {
if !self.short_lived_subscriptions.contains_key(&subnet) { self.handle_removed_subnet(subnet, SubscriptionKind::LongLived);
debug!(self.log, "Unsubscribing from subnet"; "subnet" => ?subnet, "subscription_kind" => ?SubscriptionKind::LongLived);
self.queue_event(SubnetServiceMessage::Unsubscribe(Subnet::Attestation(
subnet,
)));
}
self.queue_event(SubnetServiceMessage::EnrRemove(Subnet::Attestation(subnet)));
} }
} }
} }
/// Overwrites the long lived subscriptions for testing.
#[cfg(all(test, feature = "deterministic_long_lived_attnets"))]
pub fn set_long_lived_subscriptions(&mut self, subnets: HashSet<SubnetId>) {
self.long_lived_subscriptions = subnets
}
/// Checks if we have subscribed aggregate validators for the subnet. If not, checks the gossip /// Checks if we have subscribed aggregate validators for the subnet. If not, checks the gossip
/// verification, re-propagates and returns false. /// verification, re-propagates and returns false.
pub fn should_process_attestation( pub fn should_process_attestation(
@ -535,7 +452,7 @@ impl<T: BeaconChainTypes> AttestationService<T> {
} }
// Subscribes to the subnet if it should be done immediately, or schedules it if required. // Subscribes to the subnet if it should be done immediately, or schedules it if required.
fn subscribe_to_subnet( fn subscribe_to_short_lived_subnet(
&mut self, &mut self,
ExactSubnet { subnet_id, slot }: ExactSubnet, ExactSubnet { subnet_id, slot }: ExactSubnet,
) -> Result<(), &'static str> { ) -> Result<(), &'static str> {
@ -564,12 +481,7 @@ impl<T: BeaconChainTypes> AttestationService<T> {
// immediately. // immediately.
if time_to_subscription_start.is_zero() { if time_to_subscription_start.is_zero() {
// This is a current or past slot, we subscribe immediately. // This is a current or past slot, we subscribe immediately.
self.subscribe_to_subnet_immediately( self.subscribe_to_short_lived_subnet_immediately(subnet_id, slot + 1)?;
subnet_id,
#[cfg(not(feature = "deterministic_long_lived_attnets"))]
SubscriptionKind::ShortLived,
slot + 1,
)?;
} else { } else {
// This is a future slot, schedule subscribing. // This is a future slot, schedule subscribing.
trace!(self.log, "Scheduling subnet subscription"; "subnet" => ?subnet_id, "time_to_subscription_start" => ?time_to_subscription_start); trace!(self.log, "Scheduling subnet subscription"; "subnet" => ?subnet_id, "time_to_subscription_start" => ?time_to_subscription_start);
@ -580,79 +492,6 @@ impl<T: BeaconChainTypes> AttestationService<T> {
Ok(()) Ok(())
} }
/// Updates the `known_validators` mapping and subscribes to long lived subnets if required.
#[cfg(not(feature = "deterministic_long_lived_attnets"))]
fn add_known_validator(&mut self, validator_index: u64) {
let previously_known = self.known_validators.contains_key(&validator_index);
// Add the new validator or update the current timeout for a known validator.
self.known_validators.insert(validator_index);
if !previously_known {
// New validator has subscribed.
// Subscribe to random topics and update the ENR if needed.
self.subscribe_to_random_subnets();
}
}
/// Subscribe to long-lived random subnets and update the local ENR bitfield.
/// The number of subnets to subscribe depends on the number of active validators and number of
/// current subscriptions.
#[cfg(not(feature = "deterministic_long_lived_attnets"))]
fn subscribe_to_random_subnets(&mut self) {
if self.subscribe_all_subnets {
// This case is not handled by this service.
return;
}
let max_subnets = self.beacon_chain.spec.attestation_subnet_count;
// Calculate how many subnets we need,
let required_long_lived_subnets = {
let subnets_for_validators = self
.known_validators
.len()
.saturating_mul(self.beacon_chain.spec.random_subnets_per_validator as usize);
subnets_for_validators // How many subnets we need
.min(max_subnets as usize) // Capped by the max
.saturating_sub(self.long_lived_subscriptions.len()) // Minus those we have
};
if required_long_lived_subnets == 0 {
// Nothing to do.
return;
}
// Build a list of the subnets that we are not currently advertising.
let available_subnets = (0..max_subnets)
.map(SubnetId::new)
.filter(|subnet_id| !self.long_lived_subscriptions.contains_key(subnet_id))
.collect::<Vec<_>>();
let subnets_to_subscribe: Vec<_> = available_subnets
.choose_multiple(&mut rand::thread_rng(), required_long_lived_subnets)
.cloned()
.collect();
// Calculate in which slot does this subscription end.
let end_slot = match self.beacon_chain.slot_clock.now() {
Some(slot) => slot + self.long_lived_subnet_subscription_slots,
None => {
return debug!(
self.log,
"Failed to calculate end slot of long lived subnet subscriptions."
)
}
};
for subnet_id in &subnets_to_subscribe {
if let Err(e) = self.subscribe_to_subnet_immediately(
*subnet_id,
SubscriptionKind::LongLived,
end_slot,
) {
debug!(self.log, "Failed to subscribe to long lived subnet"; "subnet" => ?subnet_id, "err" => e);
}
}
}
/* A collection of functions that handle the various timeouts */ /* A collection of functions that handle the various timeouts */
/// Registers a subnet as subscribed. /// Registers a subnet as subscribed.
@ -662,11 +501,9 @@ impl<T: BeaconChainTypes> AttestationService<T> {
/// out the appropriate events. /// out the appropriate events.
/// ///
/// On determinist long lived subnets, this is only used for short lived subscriptions. /// On determinist long lived subnets, this is only used for short lived subscriptions.
fn subscribe_to_subnet_immediately( fn subscribe_to_short_lived_subnet_immediately(
&mut self, &mut self,
subnet_id: SubnetId, subnet_id: SubnetId,
#[cfg(not(feature = "deterministic_long_lived_attnets"))]
subscription_kind: SubscriptionKind,
end_slot: Slot, end_slot: Slot,
) -> Result<(), &'static str> { ) -> Result<(), &'static str> {
if self.subscribe_all_subnets { if self.subscribe_all_subnets {
@ -685,25 +522,12 @@ impl<T: BeaconChainTypes> AttestationService<T> {
return Err("Time when subscription would end has already passed."); return Err("Time when subscription would end has already passed.");
} }
#[cfg(feature = "deterministic_long_lived_attnets")]
let subscription_kind = SubscriptionKind::ShortLived; let subscription_kind = SubscriptionKind::ShortLived;
// We need to check and add a subscription for the right kind, regardless of the presence // We need to check and add a subscription for the right kind, regardless of the presence
// of the subnet as a subscription of the other kind. This is mainly since long lived // of the subnet as a subscription of the other kind. This is mainly since long lived
// subscriptions can be removed at any time when a validator goes offline. // subscriptions can be removed at any time when a validator goes offline.
#[cfg(not(feature = "deterministic_long_lived_attnets"))]
let (subscriptions, already_subscribed_as_other_kind) = match subscription_kind {
SubscriptionKind::ShortLived => (
&mut self.short_lived_subscriptions,
self.long_lived_subscriptions.contains_key(&subnet_id),
),
SubscriptionKind::LongLived => (
&mut self.long_lived_subscriptions,
self.short_lived_subscriptions.contains_key(&subnet_id),
),
};
#[cfg(feature = "deterministic_long_lived_attnets")]
let (subscriptions, already_subscribed_as_other_kind) = ( let (subscriptions, already_subscribed_as_other_kind) = (
&mut self.short_lived_subscriptions, &mut self.short_lived_subscriptions,
self.long_lived_subscriptions.contains(&subnet_id), self.long_lived_subscriptions.contains(&subnet_id),
@ -738,57 +562,19 @@ impl<T: BeaconChainTypes> AttestationService<T> {
subnet_id, subnet_id,
))); )));
} }
// If this is a new long lived subscription, send out the appropriate events.
#[cfg(not(feature = "deterministic_long_lived_attnets"))]
if SubscriptionKind::LongLived == subscription_kind {
let subnet = Subnet::Attestation(subnet_id);
// Advertise this subnet in our ENR.
self.long_lived_subscriptions.insert_at(
subnet_id,
end_slot,
time_to_subscription_end,
);
self.queue_event(SubnetServiceMessage::EnrAdd(subnet));
if !self.discovery_disabled {
self.queue_event(SubnetServiceMessage::DiscoverPeers(vec![
SubnetDiscovery {
subnet,
min_ttl: None,
},
]))
}
}
} }
} }
Ok(()) Ok(())
} }
/// A random subnet has expired.
///
/// This function selects a new subnet to join, or extends the expiry if there are no more
/// available subnets to choose from.
#[cfg(not(feature = "deterministic_long_lived_attnets"))]
fn handle_random_subnet_expiry(&mut self, subnet_id: SubnetId) {
self.handle_removed_subnet(subnet_id, SubscriptionKind::LongLived);
// Remove the ENR bitfield bit and choose a new random on from the available subnets
// Subscribe to a new random subnet.
self.subscribe_to_random_subnets();
}
// Unsubscribes from a subnet that was removed if it does not continue to exist as a // Unsubscribes from a subnet that was removed if it does not continue to exist as a
// subscription of the other kind. For long lived subscriptions, it also removes the // subscription of the other kind. For long lived subscriptions, it also removes the
// advertisement from our ENR. // advertisement from our ENR.
fn handle_removed_subnet(&mut self, subnet_id: SubnetId, subscription_kind: SubscriptionKind) { fn handle_removed_subnet(&mut self, subnet_id: SubnetId, subscription_kind: SubscriptionKind) {
let exists_in_other_subscriptions = match subscription_kind { let exists_in_other_subscriptions = match subscription_kind {
SubscriptionKind::LongLived => self.short_lived_subscriptions.contains_key(&subnet_id), SubscriptionKind::LongLived => self.short_lived_subscriptions.contains_key(&subnet_id),
#[cfg(feature = "deterministic_long_lived_attnets")]
SubscriptionKind::ShortLived => self.long_lived_subscriptions.contains(&subnet_id), SubscriptionKind::ShortLived => self.long_lived_subscriptions.contains(&subnet_id),
#[cfg(not(feature = "deterministic_long_lived_attnets"))]
SubscriptionKind::ShortLived => self.long_lived_subscriptions.contains_key(&subnet_id),
}; };
if !exists_in_other_subscriptions { if !exists_in_other_subscriptions {
@ -806,48 +592,6 @@ impl<T: BeaconChainTypes> AttestationService<T> {
))); )));
} }
} }
/// A known validator has not sent a subscription in a while. They are considered offline and the
/// beacon node no longer needs to be subscribed to the allocated random subnets.
///
/// We don't keep track of a specific validator to random subnet, rather the ratio of active
/// validators to random subnets. So when a validator goes offline, we can simply remove the
/// allocated amount of random subnets.
#[cfg(not(feature = "deterministic_long_lived_attnets"))]
fn handle_known_validator_expiry(&mut self) {
// Calculate how many subnets should we remove.
let extra_subnet_count = {
let max_subnets = self.beacon_chain.spec.attestation_subnet_count;
let subnets_for_validators = self
.known_validators
.len()
.saturating_mul(self.beacon_chain.spec.random_subnets_per_validator as usize)
.min(max_subnets as usize);
self.long_lived_subscriptions
.len()
.saturating_sub(subnets_for_validators)
};
if extra_subnet_count == 0 {
// Nothing to do
return;
}
let advertised_subnets = self
.long_lived_subscriptions
.keys()
.cloned()
.collect::<Vec<_>>();
let to_remove_subnets = advertised_subnets
.choose_multiple(&mut rand::thread_rng(), extra_subnet_count)
.cloned();
for subnet_id in to_remove_subnets {
self.long_lived_subscriptions.remove(&subnet_id);
self.handle_removed_subnet(subnet_id, SubscriptionKind::LongLived);
}
}
} }
impl<T: BeaconChainTypes> Stream for AttestationService<T> { impl<T: BeaconChainTypes> Stream for AttestationService<T> {
@ -868,37 +612,34 @@ impl<T: BeaconChainTypes> Stream for AttestationService<T> {
return Poll::Ready(Some(event)); return Poll::Ready(Some(event));
} }
// Process first any known validator expiries, since these affect how many long lived // If we aren't subscribed to all subnets, handle the deterministic long-lived subnets
// subnets we need. if !self.subscribe_all_subnets {
#[cfg(not(feature = "deterministic_long_lived_attnets"))]
match self.known_validators.poll_next_unpin(cx) {
Poll::Ready(Some(Ok(_validator_index))) => {
self.handle_known_validator_expiry();
}
Poll::Ready(Some(Err(e))) => {
error!(self.log, "Failed to check for random subnet cycles"; "error"=> e);
}
Poll::Ready(None) | Poll::Pending => {}
}
#[cfg(feature = "deterministic_long_lived_attnets")]
match self.next_long_lived_subscription_event.as_mut().poll(cx) { match self.next_long_lived_subscription_event.as_mut().poll(cx) {
Poll::Ready(_) => self.recompute_long_lived_subnets(), Poll::Ready(_) => {
self.recompute_long_lived_subnets();
// We re-wake the task as there could be other subscriptions to process
self.waker
.as_ref()
.expect("Waker has been set")
.wake_by_ref();
}
Poll::Pending => {} Poll::Pending => {}
} }
}
// Process scheduled subscriptions that might be ready, since those can extend a soon to // Process scheduled subscriptions that might be ready, since those can extend a soon to
// expire subscription. // expire subscription.
match self.scheduled_short_lived_subscriptions.poll_next_unpin(cx) { match self.scheduled_short_lived_subscriptions.poll_next_unpin(cx) {
Poll::Ready(Some(Ok(ExactSubnet { subnet_id, slot }))) => { Poll::Ready(Some(Ok(ExactSubnet { subnet_id, slot }))) => {
if let Err(e) = self.subscribe_to_subnet_immediately( if let Err(e) =
subnet_id, self.subscribe_to_short_lived_subnet_immediately(subnet_id, slot + 1)
#[cfg(not(feature = "deterministic_long_lived_attnets"))] {
SubscriptionKind::ShortLived,
slot + 1,
) {
debug!(self.log, "Failed to subscribe to short lived subnet"; "subnet" => ?subnet_id, "err" => e); debug!(self.log, "Failed to subscribe to short lived subnet"; "subnet" => ?subnet_id, "err" => e);
} }
self.waker
.as_ref()
.expect("Waker has been set")
.wake_by_ref();
} }
Poll::Ready(Some(Err(e))) => { Poll::Ready(Some(Err(e))) => {
error!(self.log, "Failed to check for scheduled subnet subscriptions"; "error"=> e); error!(self.log, "Failed to check for scheduled subnet subscriptions"; "error"=> e);
@ -910,6 +651,11 @@ impl<T: BeaconChainTypes> Stream for AttestationService<T> {
match self.short_lived_subscriptions.poll_next_unpin(cx) { match self.short_lived_subscriptions.poll_next_unpin(cx) {
Poll::Ready(Some(Ok((subnet_id, _end_slot)))) => { Poll::Ready(Some(Ok((subnet_id, _end_slot)))) => {
self.handle_removed_subnet(subnet_id, SubscriptionKind::ShortLived); self.handle_removed_subnet(subnet_id, SubscriptionKind::ShortLived);
// We re-wake the task as there could be other subscriptions to process
self.waker
.as_ref()
.expect("Waker has been set")
.wake_by_ref();
} }
Poll::Ready(Some(Err(e))) => { Poll::Ready(Some(Err(e))) => {
error!(self.log, "Failed to check for subnet unsubscription times"; "error"=> e); error!(self.log, "Failed to check for subnet unsubscription times"; "error"=> e);
@ -917,18 +663,6 @@ impl<T: BeaconChainTypes> Stream for AttestationService<T> {
Poll::Ready(None) | Poll::Pending => {} Poll::Ready(None) | Poll::Pending => {}
} }
// Process any random subnet expiries.
#[cfg(not(feature = "deterministic_long_lived_attnets"))]
match self.long_lived_subscriptions.poll_next_unpin(cx) {
Poll::Ready(Some(Ok((subnet_id, _end_slot)))) => {
self.handle_random_subnet_expiry(subnet_id)
}
Poll::Ready(Some(Err(e))) => {
error!(self.log, "Failed to check for random subnet cycles"; "error"=> e);
}
Poll::Ready(None) | Poll::Pending => {}
}
// Poll to remove entries on expiration, no need to act on expiration events. // Poll to remove entries on expiration, no need to act on expiration events.
if let Some(tracked_vals) = self.aggregate_validators_on_subnet.as_mut() { if let Some(tracked_vals) = self.aggregate_validators_on_subnet.as_mut() {
if let Poll::Ready(Some(Err(e))) = tracked_vals.poll_next_unpin(cx) { if let Poll::Ready(Some(Err(e))) = tracked_vals.poll_next_unpin(cx) {

View File

@ -126,10 +126,7 @@ fn get_attestation_service(
AttestationService::new( AttestationService::new(
beacon_chain, beacon_chain,
#[cfg(feature = "deterministic_long_lived_attnets")] lighthouse_network::discv5::enr::NodeId::random(),
lighthouse_network::discv5::enr::NodeId::random()
.raw()
.into(),
&config, &config,
&log, &log,
) )
@ -179,9 +176,6 @@ async fn get_events<S: Stream<Item = SubnetServiceMessage> + Unpin>(
mod attestation_service { mod attestation_service {
#[cfg(feature = "deterministic_long_lived_attnets")]
use std::collections::HashSet;
#[cfg(not(windows))] #[cfg(not(windows))]
use crate::subnet_service::attestation_subnets::MIN_PEER_DISCOVERY_SLOT_LOOK_AHEAD; use crate::subnet_service::attestation_subnets::MIN_PEER_DISCOVERY_SLOT_LOOK_AHEAD;
@ -192,8 +186,8 @@ mod attestation_service {
attestation_committee_index: CommitteeIndex, attestation_committee_index: CommitteeIndex,
slot: Slot, slot: Slot,
committee_count_at_slot: u64, committee_count_at_slot: u64,
is_aggregator: bool,
) -> ValidatorSubscription { ) -> ValidatorSubscription {
let is_aggregator = true;
ValidatorSubscription { ValidatorSubscription {
validator_index, validator_index,
attestation_committee_index, attestation_committee_index,
@ -203,11 +197,11 @@ mod attestation_service {
} }
} }
#[cfg(not(feature = "deterministic_long_lived_attnets"))]
fn get_subscriptions( fn get_subscriptions(
validator_count: u64, validator_count: u64,
slot: Slot, slot: Slot,
committee_count_at_slot: u64, committee_count_at_slot: u64,
is_aggregator: bool,
) -> Vec<ValidatorSubscription> { ) -> Vec<ValidatorSubscription> {
(0..validator_count) (0..validator_count)
.map(|validator_index| { .map(|validator_index| {
@ -216,6 +210,7 @@ mod attestation_service {
validator_index, validator_index,
slot, slot,
committee_count_at_slot, committee_count_at_slot,
is_aggregator,
) )
}) })
.collect() .collect()
@ -229,6 +224,7 @@ mod attestation_service {
// Keep a low subscription slot so that there are no additional subnet discovery events. // Keep a low subscription slot so that there are no additional subnet discovery events.
let subscription_slot = 0; let subscription_slot = 0;
let committee_count = 1; let committee_count = 1;
let subnets_per_node = MainnetEthSpec::default_spec().subnets_per_node as usize;
// create the attestation service and subscriptions // create the attestation service and subscriptions
let mut attestation_service = get_attestation_service(None); let mut attestation_service = get_attestation_service(None);
@ -243,6 +239,7 @@ mod attestation_service {
committee_index, committee_index,
current_slot + Slot::new(subscription_slot), current_slot + Slot::new(subscription_slot),
committee_count, committee_count,
true,
)]; )];
// submit the subscriptions // submit the subscriptions
@ -266,16 +263,19 @@ mod attestation_service {
// Wait for 1 slot duration to get the unsubscription event // Wait for 1 slot duration to get the unsubscription event
let events = get_events( let events = get_events(
&mut attestation_service, &mut attestation_service,
Some(5), Some(subnets_per_node * 3 + 2),
(MainnetEthSpec::slots_per_epoch() * 3) as u32, (MainnetEthSpec::slots_per_epoch() * 3) as u32,
) )
.await; .await;
matches::assert_matches!( matches::assert_matches!(
events[..3], events[..6],
[ [
SubnetServiceMessage::Subscribe(_any1), SubnetServiceMessage::Subscribe(_any1),
SubnetServiceMessage::EnrAdd(_any3), SubnetServiceMessage::EnrAdd(_any3),
SubnetServiceMessage::DiscoverPeers(_), SubnetServiceMessage::DiscoverPeers(_),
SubnetServiceMessage::Subscribe(_),
SubnetServiceMessage::EnrAdd(_),
SubnetServiceMessage::DiscoverPeers(_),
] ]
); );
@ -284,10 +284,10 @@ mod attestation_service {
if !attestation_service if !attestation_service
.is_subscribed(&subnet_id, attestation_subnets::SubscriptionKind::LongLived) .is_subscribed(&subnet_id, attestation_subnets::SubscriptionKind::LongLived)
{ {
assert_eq!(expected[..], events[3..]); assert_eq!(expected[..], events[subnets_per_node * 3..]);
} }
// Should be subscribed to only 1 long lived subnet after unsubscription. // Should be subscribed to only subnets_per_node long lived subnet after unsubscription.
assert_eq!(attestation_service.subscription_count(), 1); assert_eq!(attestation_service.subscription_count(), subnets_per_node);
} }
/// Test to verify that we are not unsubscribing to a subnet before a required subscription. /// Test to verify that we are not unsubscribing to a subnet before a required subscription.
@ -297,6 +297,7 @@ mod attestation_service {
// subscription config // subscription config
let validator_index = 1; let validator_index = 1;
let committee_count = 1; let committee_count = 1;
let subnets_per_node = MainnetEthSpec::default_spec().subnets_per_node as usize;
// Makes 2 validator subscriptions to the same subnet but at different slots. // Makes 2 validator subscriptions to the same subnet but at different slots.
// There should be just 1 unsubscription event for the later slot subscription (subscription_slot2). // There should be just 1 unsubscription event for the later slot subscription (subscription_slot2).
@ -318,6 +319,7 @@ mod attestation_service {
com1, com1,
current_slot + Slot::new(subscription_slot1), current_slot + Slot::new(subscription_slot1),
committee_count, committee_count,
true,
); );
let sub2 = get_subscription( let sub2 = get_subscription(
@ -325,6 +327,7 @@ mod attestation_service {
com2, com2,
current_slot + Slot::new(subscription_slot2), current_slot + Slot::new(subscription_slot2),
committee_count, committee_count,
true,
); );
let subnet_id1 = SubnetId::compute_subnet::<MainnetEthSpec>( let subnet_id1 = SubnetId::compute_subnet::<MainnetEthSpec>(
@ -366,16 +369,22 @@ mod attestation_service {
let expected = SubnetServiceMessage::Subscribe(Subnet::Attestation(subnet_id1)); let expected = SubnetServiceMessage::Subscribe(Subnet::Attestation(subnet_id1));
// Should be still subscribed to 1 long lived and 1 short lived subnet if both are // Should be still subscribed to 2 long lived and up to 1 short lived subnet if both are
// different. // different.
if !attestation_service.is_subscribed( if !attestation_service.is_subscribed(
&subnet_id1, &subnet_id1,
attestation_subnets::SubscriptionKind::LongLived, attestation_subnets::SubscriptionKind::LongLived,
) { ) {
assert_eq!(expected, events[3]); // The index is 3*subnets_per_node (because we subscribe + discover + enr per long lived
assert_eq!(attestation_service.subscription_count(), 2); // subnet) + 1
let index = 3 * subnets_per_node;
assert_eq!(expected, events[index]);
assert_eq!(
attestation_service.subscription_count(),
subnets_per_node + 1
);
} else { } else {
assert_eq!(attestation_service.subscription_count(), 1); assert!(attestation_service.subscription_count() == subnets_per_node);
} }
// Get event for 1 more slot duration, we should get the unsubscribe event now. // Get event for 1 more slot duration, we should get the unsubscribe event now.
@ -395,17 +404,17 @@ mod attestation_service {
); );
} }
// Should be subscribed to only 1 long lived subnet after unsubscription. // Should be subscribed 2 long lived subnet after unsubscription.
assert_eq!(attestation_service.subscription_count(), 1); assert_eq!(attestation_service.subscription_count(), subnets_per_node);
} }
#[cfg(not(feature = "deterministic_long_lived_attnets"))]
#[tokio::test] #[tokio::test]
async fn subscribe_all_random_subnets() { async fn subscribe_all_subnets() {
let attestation_subnet_count = MainnetEthSpec::default_spec().attestation_subnet_count; let attestation_subnet_count = MainnetEthSpec::default_spec().attestation_subnet_count;
let subscription_slot = 10; let subscription_slot = 3;
let subscription_count = attestation_subnet_count; let subscription_count = attestation_subnet_count;
let committee_count = 1; let committee_count = 1;
let subnets_per_node = MainnetEthSpec::default_spec().subnets_per_node as usize;
// create the attestation service and subscriptions // create the attestation service and subscriptions
let mut attestation_service = get_attestation_service(None); let mut attestation_service = get_attestation_service(None);
@ -419,6 +428,7 @@ mod attestation_service {
subscription_count, subscription_count,
current_slot + subscription_slot, current_slot + subscription_slot,
committee_count, committee_count,
true,
); );
// submit the subscriptions // submit the subscriptions
@ -426,42 +436,52 @@ mod attestation_service {
.validator_subscriptions(subscriptions) .validator_subscriptions(subscriptions)
.unwrap(); .unwrap();
let events = get_events(&mut attestation_service, None, 3).await; let events = get_events(&mut attestation_service, Some(131), 10).await;
let mut discover_peer_count = 0; let mut discover_peer_count = 0;
let mut enr_add_count = 0; let mut enr_add_count = 0;
let mut unexpected_msg_count = 0; let mut unexpected_msg_count = 0;
let mut unsubscribe_event_count = 0;
for event in &events { for event in &events {
match event { match event {
SubnetServiceMessage::DiscoverPeers(_) => discover_peer_count += 1, SubnetServiceMessage::DiscoverPeers(_) => discover_peer_count += 1,
SubnetServiceMessage::Subscribe(_any_subnet) => {} SubnetServiceMessage::Subscribe(_any_subnet) => {}
SubnetServiceMessage::EnrAdd(_any_subnet) => enr_add_count += 1, SubnetServiceMessage::EnrAdd(_any_subnet) => enr_add_count += 1,
SubnetServiceMessage::Unsubscribe(_) => unsubscribe_event_count += 1,
_ => unexpected_msg_count += 1, _ => unexpected_msg_count += 1,
} }
} }
// There should be a Subscribe Event, and Enr Add event and a DiscoverPeers event for each
// long-lived subnet initially. The next event should be a bulk discovery event.
let bulk_discovery_index = 3 * subnets_per_node;
// The bulk discovery request length should be equal to validator_count // The bulk discovery request length should be equal to validator_count
let bulk_discovery_event = events.last().unwrap(); let bulk_discovery_event = &events[bulk_discovery_index];
if let SubnetServiceMessage::DiscoverPeers(d) = bulk_discovery_event { if let SubnetServiceMessage::DiscoverPeers(d) = bulk_discovery_event {
assert_eq!(d.len(), attestation_subnet_count as usize); assert_eq!(d.len(), attestation_subnet_count as usize);
} else { } else {
panic!("Unexpected event {:?}", bulk_discovery_event); panic!("Unexpected event {:?}", bulk_discovery_event);
} }
// 64 `DiscoverPeer` requests of length 1 corresponding to random subnets // 64 `DiscoverPeer` requests of length 1 corresponding to deterministic subnets
// and 1 `DiscoverPeer` request corresponding to bulk subnet discovery. // and 1 `DiscoverPeer` request corresponding to bulk subnet discovery.
assert_eq!(discover_peer_count, subscription_count + 1); assert_eq!(discover_peer_count, subnets_per_node + 1);
assert_eq!(attestation_service.subscription_count(), 64); assert_eq!(attestation_service.subscription_count(), subnets_per_node);
assert_eq!(enr_add_count, 64); assert_eq!(enr_add_count, subnets_per_node);
assert_eq!(
unsubscribe_event_count,
attestation_subnet_count - subnets_per_node as u64
);
assert_eq!(unexpected_msg_count, 0); assert_eq!(unexpected_msg_count, 0);
// test completed successfully // test completed successfully
} }
#[cfg(not(feature = "deterministic_long_lived_attnets"))]
#[tokio::test] #[tokio::test]
async fn subscribe_all_random_subnets_plus_one() { async fn subscribe_correct_number_of_subnets() {
let attestation_subnet_count = MainnetEthSpec::default_spec().attestation_subnet_count; let attestation_subnet_count = MainnetEthSpec::default_spec().attestation_subnet_count;
let subscription_slot = 10; let subscription_slot = 10;
let subnets_per_node = MainnetEthSpec::default_spec().subnets_per_node as usize;
// the 65th subscription should result in no more messages than the previous scenario // the 65th subscription should result in no more messages than the previous scenario
let subscription_count = attestation_subnet_count + 1; let subscription_count = attestation_subnet_count + 1;
let committee_count = 1; let committee_count = 1;
@ -478,6 +498,7 @@ mod attestation_service {
subscription_count, subscription_count,
current_slot + subscription_slot, current_slot + subscription_slot,
committee_count, committee_count,
true,
); );
// submit the subscriptions // submit the subscriptions
@ -506,12 +527,12 @@ mod attestation_service {
} else { } else {
panic!("Unexpected event {:?}", bulk_discovery_event); panic!("Unexpected event {:?}", bulk_discovery_event);
} }
// 64 `DiscoverPeer` requests of length 1 corresponding to random subnets // subnets_per_node `DiscoverPeer` requests of length 1 corresponding to long-lived subnets
// and 1 `DiscoverPeer` request corresponding to the bulk subnet discovery. // and 1 `DiscoverPeer` request corresponding to the bulk subnet discovery.
// For the 65th subscription, the call to `subscribe_to_random_subnets` is not made because we are at capacity.
assert_eq!(discover_peer_count, 64 + 1); assert_eq!(discover_peer_count, subnets_per_node + 1);
assert_eq!(attestation_service.subscription_count(), 64); assert_eq!(attestation_service.subscription_count(), subnets_per_node);
assert_eq!(enr_add_count, 64); assert_eq!(enr_add_count, subnets_per_node);
assert_eq!(unexpected_msg_count, 0); assert_eq!(unexpected_msg_count, 0);
} }
@ -521,6 +542,7 @@ mod attestation_service {
// subscription config // subscription config
let validator_index = 1; let validator_index = 1;
let committee_count = 1; let committee_count = 1;
let subnets_per_node = MainnetEthSpec::default_spec().subnets_per_node as usize;
// Makes 2 validator subscriptions to the same subnet but at different slots. // Makes 2 validator subscriptions to the same subnet but at different slots.
// There should be just 1 unsubscription event for the later slot subscription (subscription_slot2). // There should be just 1 unsubscription event for the later slot subscription (subscription_slot2).
@ -542,6 +564,7 @@ mod attestation_service {
com1, com1,
current_slot + Slot::new(subscription_slot1), current_slot + Slot::new(subscription_slot1),
committee_count, committee_count,
true,
); );
let sub2 = get_subscription( let sub2 = get_subscription(
@ -549,6 +572,7 @@ mod attestation_service {
com2, com2,
current_slot + Slot::new(subscription_slot2), current_slot + Slot::new(subscription_slot2),
committee_count, committee_count,
true,
); );
let subnet_id1 = SubnetId::compute_subnet::<MainnetEthSpec>( let subnet_id1 = SubnetId::compute_subnet::<MainnetEthSpec>(
@ -596,11 +620,10 @@ mod attestation_service {
&subnet_id1, &subnet_id1,
attestation_subnets::SubscriptionKind::LongLived, attestation_subnets::SubscriptionKind::LongLived,
) { ) {
assert_eq!(expected_subscription, events[3]); assert_eq!(expected_subscription, events[subnets_per_node * 3]);
// fourth is a discovery event assert_eq!(expected_unsubscription, events[subnets_per_node * 3 + 2]);
assert_eq!(expected_unsubscription, events[5]);
} }
assert_eq!(attestation_service.subscription_count(), 1); assert_eq!(attestation_service.subscription_count(), 2);
println!("{events:?}"); println!("{events:?}");
let subscription_slot = current_slot + subscription_slot2 - 1; // one less do to the let subscription_slot = current_slot + subscription_slot2 - 1; // one less do to the
@ -633,40 +656,44 @@ mod attestation_service {
} }
#[tokio::test] #[tokio::test]
#[cfg(feature = "deterministic_long_lived_attnets")]
async fn test_update_deterministic_long_lived_subnets() { async fn test_update_deterministic_long_lived_subnets() {
let mut attestation_service = get_attestation_service(None); let mut attestation_service = get_attestation_service(None);
let new_subnet = SubnetId::new(1); let subnets_per_node = MainnetEthSpec::default_spec().subnets_per_node as usize;
let maintained_subnet = SubnetId::new(2);
let removed_subnet = SubnetId::new(3);
let current_slot = attestation_service
.beacon_chain
.slot_clock
.now()
.expect("Could not get current slot");
let subscriptions = get_subscriptions(20, current_slot, 30, false);
// submit the subscriptions
attestation_service attestation_service
.set_long_lived_subscriptions(HashSet::from([removed_subnet, maintained_subnet])); .validator_subscriptions(subscriptions)
// clear initial events .unwrap();
let _events = get_events(&mut attestation_service, None, 1).await;
attestation_service // There should only be the same subscriptions as there are in the specification,
.update_long_lived_subnets_testing(HashSet::from([maintained_subnet, new_subnet])); // regardless of subscriptions
let events = get_events(&mut attestation_service, None, 1).await;
let new_subnet = Subnet::Attestation(new_subnet);
let removed_subnet = Subnet::Attestation(removed_subnet);
assert_eq!( assert_eq!(
events, attestation_service.long_lived_subscriptions().len(),
subnets_per_node
);
let events = get_events(&mut attestation_service, None, 4).await;
// Check that we attempt to subscribe and register ENRs
matches::assert_matches!(
events[..6],
[ [
// events for the new subnet SubnetServiceMessage::Subscribe(_),
SubnetServiceMessage::Subscribe(new_subnet), SubnetServiceMessage::EnrAdd(_),
SubnetServiceMessage::EnrAdd(new_subnet), SubnetServiceMessage::DiscoverPeers(_),
SubnetServiceMessage::DiscoverPeers(vec![SubnetDiscovery { SubnetServiceMessage::Subscribe(_),
subnet: new_subnet, SubnetServiceMessage::EnrAdd(_),
min_ttl: None SubnetServiceMessage::DiscoverPeers(_),
}]),
// events for the removed subnet
SubnetServiceMessage::Unsubscribe(removed_subnet),
SubnetServiceMessage::EnrRemove(removed_subnet),
] ]
); );
println!("{events:?}")
} }
} }

View File

@ -289,7 +289,23 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
for a beacon node being referenced by validator client using the --proposer-node flag. This configuration is for enabling more secure setups.") for a beacon node being referenced by validator client using the --proposer-node flag. This configuration is for enabling more secure setups.")
.takes_value(false), .takes_value(false),
) )
.arg(
Arg::with_name("inbound-rate-limiter")
.long("inbound-rate-limiter")
.help(
"Configures the inbound rate limiter (requests received by this node).\
\
Rate limit quotas per protocol can be set in the form of \
<protocol_name>:<tokens>/<time_in_seconds>. To set quotas for multiple protocols, \
separate them by ';'. If the inbound rate limiter is enabled and a protocol is not \
present in the configuration, the default quotas will be used. \
\
This is enabled by default, using default quotas. To disable rate limiting pass \
`disabled` to this option instead."
)
.takes_value(true)
.hidden(true)
)
.arg( .arg(
Arg::with_name("disable-backfill-rate-limiting") Arg::with_name("disable-backfill-rate-limiting")
.long("disable-backfill-rate-limiting") .long("disable-backfill-rate-limiting")

View File

@ -1269,6 +1269,7 @@ pub fn set_network_config(
// Light client server config. // Light client server config.
config.enable_light_client_server = cli_args.is_present("light-client-server"); config.enable_light_client_server = cli_args.is_present("light-client-server");
// The self limiter is disabled by default.
// This flag can be used both with or without a value. Try to parse it first with a value, if // This flag can be used both with or without a value. Try to parse it first with a value, if
// no value is defined but the flag is present, use the default params. // no value is defined but the flag is present, use the default params.
config.outbound_rate_limiter_config = clap_utils::parse_optional(cli_args, "self-limiter")?; config.outbound_rate_limiter_config = clap_utils::parse_optional(cli_args, "self-limiter")?;
@ -1289,7 +1290,22 @@ pub fn set_network_config(
config.proposer_only = true; config.proposer_only = true;
warn!(log, "Proposer-only mode enabled"; "info"=> "Do not connect a validator client to this node unless via the --proposer-nodes flag"); warn!(log, "Proposer-only mode enabled"; "info"=> "Do not connect a validator client to this node unless via the --proposer-nodes flag");
} }
// The inbound rate limiter is enabled by default unless `disabled` is passed to the
// `inbound-rate-limiter` flag. Any other value should be parsed as a configuration string.
config.inbound_rate_limiter_config = match cli_args.value_of("inbound-rate-limiter") {
None => {
// Enabled by default, with default values
Some(Default::default())
}
Some("disabled") => {
// Explicitly disabled
None
}
Some(config_str) => {
// Enabled with a custom configuration
Some(config_str.parse()?)
}
};
Ok(()) Ok(())
} }

View File

@ -18,9 +18,10 @@
* [Validator Monitoring](./validator-monitoring.md) * [Validator Monitoring](./validator-monitoring.md)
* [Doppelganger Protection](./validator-doppelganger.md) * [Doppelganger Protection](./validator-doppelganger.md)
* [Suggested Fee Recipient](./suggested-fee-recipient.md) * [Suggested Fee Recipient](./suggested-fee-recipient.md)
* [Validator Graffiti](./graffiti.md)
* [APIs](./api.md) * [APIs](./api.md)
* [Beacon Node API](./api-bn.md) * [Beacon Node API](./api-bn.md)
* [/lighthouse](./api-lighthouse.md) * [Lighthouse API](./api-lighthouse.md)
* [Validator Inclusion APIs](./validator-inclusion.md) * [Validator Inclusion APIs](./validator-inclusion.md)
* [Validator Client API](./api-vc.md) * [Validator Client API](./api-vc.md)
* [Endpoints](./api-vc-endpoints.md) * [Endpoints](./api-vc-endpoints.md)
@ -36,7 +37,6 @@
* [Advanced Usage](./advanced.md) * [Advanced Usage](./advanced.md)
* [Checkpoint Sync](./checkpoint-sync.md) * [Checkpoint Sync](./checkpoint-sync.md)
* [Custom Data Directories](./advanced-datadir.md) * [Custom Data Directories](./advanced-datadir.md)
* [Validator Graffiti](./graffiti.md)
* [Proposer Only Beacon Nodes](./advanced-proposer-only.md) * [Proposer Only Beacon Nodes](./advanced-proposer-only.md)
* [Remote Signing with Web3Signer](./validator-web3signer.md) * [Remote Signing with Web3Signer](./validator-web3signer.md)
* [Database Configuration](./advanced_database.md) * [Database Configuration](./advanced_database.md)
@ -46,9 +46,8 @@
* [Advanced Networking](./advanced_networking.md) * [Advanced Networking](./advanced_networking.md)
* [Running a Slasher](./slasher.md) * [Running a Slasher](./slasher.md)
* [Redundancy](./redundancy.md) * [Redundancy](./redundancy.md)
* [Pre-Releases](./advanced-pre-releases.md)
* [Release Candidates](./advanced-release-candidates.md) * [Release Candidates](./advanced-release-candidates.md)
* [MEV and Lighthouse](./builders.md) * [Maximal Extractable Value (MEV)](./builders.md)
* [Merge Migration](./merge-migration.md) * [Merge Migration](./merge-migration.md)
* [Late Block Re-orgs](./late-block-re-orgs.md) * [Late Block Re-orgs](./late-block-re-orgs.md)
* [Contributing](./contributing.md) * [Contributing](./contributing.md)

View File

@ -1,4 +0,0 @@
# Pre-Releases
Pre-releases are now referred to as [Release Candidates](./advanced-release-candidates.md). The terms may
be used interchangeably.

View File

@ -7,7 +7,7 @@
[`v1.4.0`]: https://github.com/sigp/lighthouse/releases/tag/v1.4.0 [`v1.4.0`]: https://github.com/sigp/lighthouse/releases/tag/v1.4.0
From time-to-time, Lighthouse *release candidates* will be published on the [sigp/lighthouse] From time-to-time, Lighthouse *release candidates* will be published on the [sigp/lighthouse]
repository. These releases have passed the usual automated testing, however the developers would repository. Release candidates are previously known as Pre-Releases. These releases have passed the usual automated testing, however the developers would
like to see it running "in the wild" in a variety of configurations before declaring it an official, like to see it running "in the wild" in a variety of configurations before declaring it an official,
stable release. Release candidates are also used by developers to get feedback from users regarding the stable release. Release candidates are also used by developers to get feedback from users regarding the
ergonomics of new features or changes. ergonomics of new features or changes.
@ -36,8 +36,9 @@ Users may wish to try a release candidate for the following reasons:
- To help detect bugs and regressions before they reach production. - To help detect bugs and regressions before they reach production.
- To provide feedback on annoyances before they make it into a release and become harder to change or revert. - To provide feedback on annoyances before they make it into a release and become harder to change or revert.
There can also be a scenario that a bug has been found and requires an urgent fix. An example of incidence is [v4.0.2-rc.0](https://github.com/sigp/lighthouse/releases/tag/v4.0.2-rc.0) which contains a hot-fix to address high CPU usage experienced after the [Capella](https://ethereum.org/en/history/#capella) upgrade on 12<sup>th</sup> April 2023. In this scenario, we will announce the release candidate on [Github](https://github.com/sigp/lighthouse/releases) and also on [Discord](https://discord.gg/cyAszAh) to recommend users to update to the release candidate version.
## When *not* to use a release candidate ## When *not* to use a release candidate
It is not recommended to use release candidates for any critical tasks on mainnet (e.g., staking). Other than the above scenarios, it is generally not recommended to use release candidates for any critical tasks on mainnet (e.g., staking). To test new release candidate features, try one of the testnets (e.g., Goerli).
To test critical features, try one of the testnets (e.g., Prater).

View File

@ -6,4 +6,18 @@ elsewhere?
This section provides detailed information about configuring Lighthouse for specific use cases, and This section provides detailed information about configuring Lighthouse for specific use cases, and
tips about how things work under the hood. tips about how things work under the hood.
* [Advanced Database Configuration](./advanced_database.md): understanding space-time trade-offs in the database. * [Checkpoint Sync](./checkpoint-sync.md): quickly sync the beacon chain to perform validator duties.
* [Custom Data Directories](./advanced-datadir.md): modify the data directory to your preferred location.
* [Proposer Only Beacon Nodes](./advanced-proposer-only.md): beacon node only for proposer duty for increased anonymity.
* [Remote Signing with Web3Signer](./validator-web3signer.md): don't want to store your keystore in local node? Use web3signer.
* [Database Configuration](./advanced_database.md): understanding space-time trade-offs in the database.
* [Database Migrations](./database-migrations.md): have a look at all previous Lighthouse database scheme versions.
* [Key Management](./key-management.md): explore how to generate wallet with Lighthouse.
* [Key Recovery](./key-recovery.md): explore how to recover wallet and validator with Lighthouse.
* [Advanced Networking](./advanced_networking.md): open your ports to have a diverse and healthy set of peers.
* [Running a Slasher](./slasher.md): contribute to the health of the network by running a slasher.
* [Redundancy](./redundancy.md): want to have more than one beacon node as backup? This is for you.
* [Release Candidates](./advanced-release-candidates.md): latest release of Lighthouse to get feedback from users.
* [Maximal Extractable Value](./builders.md): use external builders for a potential higher rewards during block proposals
* [Merge Migration](./merge-migration.md): look at what you need to do during a significant network upgrade: The Merge
* [Late Block Re-orgs](./late-block-re-orgs.md): read information about Lighthouse late block re-orgs.

View File

@ -23,13 +23,17 @@ states to slow down dramatically. A lower _slots per restore point_ value (SPRP)
frequent restore points, while a higher SPRP corresponds to less frequent. The table below shows frequent restore points, while a higher SPRP corresponds to less frequent. The table below shows
some example values. some example values.
| Use Case | SPRP | Yearly Disk Usage | Load Historical State | | Use Case | SPRP | Yearly Disk Usage* | Load Historical State |
|--------------------------|------|-------------------|-----------------------| |----------------------------|------|-------------------|-----------------------|
| Block explorer/analysis | 32 | 1.4 TB | 155 ms | | Research | 32 | 3.4 TB | 155 ms |
| Hobbyist (prev. default) | 2048 | 23.1 GB | 10.2 s | | Block explorer/analysis | 128 | 851 GB | 620 ms |
| Validator only (default) | 8192 | 5.7 GB | 41 s | | Enthusiast (prev. default) | 2048 | 53.6 GB | 10.2 s |
| EHobbyist | 4096 | 26.8 GB | 20.5 s |
| Validator only (default) | 8192 | 8.1 GB | 41 s |
As you can see, it's a high-stakes trade-off! The relationships to disk usage and historical state *Last update: May 2023.
As we can see, it's a high-stakes trade-off! The relationships to disk usage and historical state
load time are both linear doubling SPRP halves disk usage and doubles load time. The minimum SPRP load time are both linear doubling SPRP halves disk usage and doubles load time. The minimum SPRP
is 32, and the maximum is 8192. is 32, and the maximum is 8192.
@ -38,10 +42,12 @@ The default value is 8192 for databases synced from scratch using Lighthouse v2.
The values shown in the table are approximate, calculated using a simple heuristic: each The values shown in the table are approximate, calculated using a simple heuristic: each
`BeaconState` consumes around 18MB of disk space, and each block replayed takes around 5ms. The `BeaconState` consumes around 18MB of disk space, and each block replayed takes around 5ms. The
**Yearly Disk Usage** column shows the approx size of the freezer DB _alone_ (hot DB not included), **Yearly Disk Usage** column shows the approximate size of the freezer DB _alone_ (hot DB not included), calculated proportionally using the total freezer database disk usage.
and the **Load Historical State** time is the worst-case load time for a state in the last slot The **Load Historical State** time is the worst-case load time for a state in the last slot
before a restore point. before a restore point.
As an example, we use an SPRP of 4096 to calculate the total size of the freezer database until May 2023. It has been about 900 days since the genesis, the total disk usage by the freezer database is therefore: 900/365*26.8 GB = 66 GB.
### Defaults ### Defaults
As of Lighthouse v2.2.0, the default slots-per-restore-point value has been increased from 2048 As of Lighthouse v2.2.0, the default slots-per-restore-point value has been increased from 2048
@ -68,6 +74,8 @@ The historical state cache size can be specified with the flag `--historic-state
lighthouse beacon_node --historic-state-cache-size 4 lighthouse beacon_node --historic-state-cache-size 4
``` ```
> Note: This feature will cause high memory usage.
## Glossary ## Glossary
* _Freezer DB_: part of the database storing finalized states. States are stored in a sparser * _Freezer DB_: part of the database storing finalized states. States are stored in a sparser

View File

@ -51,7 +51,7 @@ peers for your node and overall improve the Ethereum consensus network.
Lighthouse currently supports UPnP. If UPnP is enabled on your router, Lighthouse currently supports UPnP. If UPnP is enabled on your router,
Lighthouse will automatically establish the port mappings for you (the beacon Lighthouse will automatically establish the port mappings for you (the beacon
node will inform you of established routes in this case). If UPnP is not node will inform you of established routes in this case). If UPnP is not
enabled, we recommend you manually set up port mappings to both of Lighthouse's enabled, we recommend you to manually set up port mappings to both of Lighthouse's
TCP and UDP ports (9000 by default). TCP and UDP ports (9000 by default).
> Note: Lighthouse needs to advertise its publicly accessible ports in > Note: Lighthouse needs to advertise its publicly accessible ports in
@ -63,6 +63,28 @@ TCP and UDP ports (9000 by default).
> explicitly specify them using the `--enr-tcp-port` and `--enr-udp-port` as > explicitly specify them using the `--enr-tcp-port` and `--enr-udp-port` as
> explained in the following section. > explained in the following section.
### How to Open Ports
The steps to do port forwarding depends on the router, but the general steps are given below:
1. Determine the default gateway IP:
- On Linux: open a terminal and run `ip route | grep default`, the result should look something similar to `default via 192.168.50.1 dev wlp2s0 proto dhcp metric 600`. The `192.168.50.1` is your router management default gateway IP.
- On MacOS: open a terminal and run `netstat -nr|grep default` and it should return the default gateway IP.
- On Windows: open a command prompt and run `ipconfig` and look for the `Default Gateway` which will show you the gateway IP.
The default gateway IP usually looks like 192.168.X.X. Once you obtain the IP, enter it to a web browser and it will lead you to the router management page.
2. Login to the router management page. The login credentials are usually available in the manual or the router, or it can be found on a sticker underneath the router. You can also try the login credentials for some common router brands listed [here](https://www.noip.com/support/knowledgebase/general-port-forwarding-guide/).
3. Navigate to the port forward settings in your router. The exact step depends on the router, but typically it will fall under the "Advanced" section, under the name "port forwarding" or "virtual server".
4. Configure a port forwarding rule as below:
- Protocol: select `TCP/UDP` or `BOTH`
- External port: `9000`
- Internal port: `9000`
- IP address: Usually there is a dropdown list for you to select the device. Choose the device that is running Lighthouse
5. To check that you have successfully open the ports, go to [yougetsignal](https://www.yougetsignal.com/tools/open-ports/) and enter `9000` in the `port number`. If it shows "open", then you have successfully set up port forwarding. If it shows "closed", double check your settings, and also check that you have allowed firewall rules on port 9000.
### ENR Configuration ### ENR Configuration
@ -81,7 +103,7 @@ and if it is, it will update your ENR to the correct public IP and port address
(meaning you do not need to set it manually). Lighthouse persists its ENR, so (meaning you do not need to set it manually). Lighthouse persists its ENR, so
on reboot it will re-load the settings it had discovered previously. on reboot it will re-load the settings it had discovered previously.
Modifying the ENR settings can degrade the discovery of your node making it Modifying the ENR settings can degrade the discovery of your node, making it
harder for peers to find you or potentially making it harder for other peers to harder for peers to find you or potentially making it harder for other peers to
find each other. We recommend not touching these settings unless for a more find each other. We recommend not touching these settings unless for a more
advanced use case. advanced use case.

View File

@ -5,7 +5,7 @@ specification][OpenAPI]. Please follow that link for a full description of each
## Starting the server ## Starting the server
A Lighthouse beacon node can be configured to expose a HTTP server by supplying the `--http` flag. The default listen address is `127.0.0.1:5052`. A Lighthouse beacon node can be configured to expose an HTTP server by supplying the `--http` flag. The default listen address is `http://127.0.0.1:5052`.
The following CLI flags control the HTTP server: The following CLI flags control the HTTP server:
@ -55,11 +55,8 @@ Additional risks to be aware of include:
## CLI Example ## CLI Example
Start the beacon node with the HTTP server listening on [http://localhost:5052](http://localhost:5052): Start a beacon node and an execution node according to [Run a node](./run_a_node.md). Note that since [The Merge](https://ethereum.org/en/roadmap/merge/), an execution client is required to be running along with a beacon node. Hence, the query on Beacon Node APIs requires users to run both. While there are some Beacon Node APIs that you can query with only the beacon node, such as the [node version](https://ethereum.github.io/beacon-APIs/#/Node/getNodeVersion), in general an execution client is required to get the updated information about the beacon chain, such as [state root](https://ethereum.github.io/beacon-APIs/#/Beacon/getStateRoot), [headers](https://ethereum.github.io/beacon-APIs/#/Beacon/getBlockHeaders) and many others, which are dynamically progressing with time.
```bash
lighthouse bn --http
```
## HTTP Request/Response Examples ## HTTP Request/Response Examples
@ -77,40 +74,46 @@ curl -X GET "http://localhost:5052/eth/v1/beacon/headers/head" -H "accept: appl
```json ```json
{ {
"execution_optimistic": false,
"finalized": false,
"data": { "data": {
"root": "0x4381454174fc28c7095077e959dcab407ae5717b5dca447e74c340c1b743d7b2", "root": "0x9059bbed6b8891e0ba2f656dbff93fc40f8c7b2b7af8fea9df83cfce5ee5e3d8",
"canonical": true, "canonical": true,
"header": { "header": {
"message": { "message": {
"slot": "3199", "slot": "6271829",
"proposer_index": "19077", "proposer_index": "114398",
"parent_root": "0xf1934973041c5896d0d608e52847c3cd9a5f809c59c64e76f6020e3d7cd0c7cd", "parent_root": "0x1d2b4fa8247f754a7a86d36e1d0283a5e425491c431533716764880a7611d225",
"state_root": "0xe8e468f9f5961655dde91968f66480868dab8d4147de9498111df2b7e4e6fe60", "state_root": "0x2b48adea290712f56b517658dde2da5d36ee01c41aebe7af62b7873b366de245",
"body_root": "0x6f183abc6c4e97f832900b00d4e08d4373bfdc819055d76b0f4ff850f559b883" "body_root": "0x6fa74c995ce6f397fa293666cde054d6a9741f7ec280c640bee51220b4641e2d"
}, },
"signature": "0x988064a2f9cf13fe3aae051a3d85f6a4bca5a8ff6196f2f504e32f1203b549d5f86a39c6509f7113678880701b1881b50925a0417c1c88a750c8da7cd302dda5aabae4b941e3104d0cf19f5043c4f22a7d75d0d50dad5dbdaf6991381dc159ab" "signature": "0x8258e64fea426033676a0045c50543978bf173114ba94822b12188e23cbc8d8e89e0b5c628a881bf3075d325bc11341105a4e3f9332ac031d89a93b422525b79e99325928a5262f17dfa6cc3ddf84ca2466fcad86a3c168af0d045f79ef52036"
} }
} }
} }
``` ```
The `jq` tool is used to format the JSON data properly. If it returns `jq: command not found`, then you can install `jq` with `sudo apt install -y jq`. After that, run the command again, and it should return the head state of the beacon chain.
### View the status of a validator ### View the status of a validator
Shows the status of validator at index `1` at the `head` state. Shows the status of validator at index `1` at the `head` state.
```bash ```bash
curl -X GET "http://localhost:5052/eth/v1/beacon/states/head/validators/1" -H "accept: application/json" | jq curl -X GET "http://localhost:5052/eth/v1/beacon/states/head/validators/1" -H "accept: application/json"
``` ```
```json ```json
{ {
"execution_optimistic": false,
"finalized": false,
"data": { "data": {
"index": "1", "index": "1",
"balance": "63985937939", "balance": "32004587169",
"status": "Active", "status": "active_ongoing",
"validator": { "validator": {
"pubkey": "0x873e73ee8b3e4fcf1d2fb0f1036ba996ac9910b5b348f6438b5f8ef50857d4da9075d0218a9d1b99a9eae235a39703e1", "pubkey": "0xa1d1ad0714035353258038e964ae9675dc0252ee22cea896825c01458e1807bfad2f9969338798548d9858a571f7425c",
"withdrawal_credentials": "0x00b8cdcf79ba7e74300a07e9d8f8121dd0d8dd11dcfd6d3f2807c45b426ac968", "withdrawal_credentials": "0x01000000000000000000000015f4b914a0ccd14333d850ff311d6dafbfbaa32b",
"effective_balance": "32000000000", "effective_balance": "32000000000",
"slashed": false, "slashed": false,
"activation_eligibility_epoch": "0", "activation_eligibility_epoch": "0",
@ -121,6 +124,7 @@ curl -X GET "http://localhost:5052/eth/v1/beacon/states/head/validators/1" -H "
} }
} }
``` ```
You can replace `1` in the above command with the validator index that you would like to query. Other API query can be done similarly by changing the link according to the Beacon API.
## Serving the HTTP API over TLS ## Serving the HTTP API over TLS
> **Warning**: This feature is currently experimental. > **Warning**: This feature is currently experimental.
@ -147,9 +151,18 @@ openssl req -x509 -nodes -newkey rsa:4096 -keyout key.pem -out cert.pem -days 36
Note that currently Lighthouse only accepts keys that are not password protected. Note that currently Lighthouse only accepts keys that are not password protected.
This means we need to run with the `-nodes` flag (short for 'no DES'). This means we need to run with the `-nodes` flag (short for 'no DES').
Once generated, we can run Lighthouse: Once generated, we can run Lighthouse and an execution node according to [Run a node](./run_a_node.md). In addition, add the flags `--http-enable-tls --http-tls-cert cert.pem --http-tls-key key.pem` to Lighthouse, the command should look like:
```bash ```bash
lighthouse bn --http --http-enable-tls --http-tls-cert cert.pem --http-tls-key key.pem lighthouse bn \
--network mainnet \
--execution-endpoint http://localhost:8551 \
--execution-jwt /secrets/jwt.hex \
--checkpoint-sync-url https://mainnet.checkpoint.sigp.io \
--http \
--http-enable-tls \
--http-tls-cert cert.pem \
--http-tls-key key.pem
``` ```
Note that the user running Lighthouse must have permission to read the Note that the user running Lighthouse must have permission to read the
certificate and key. certificate and key.
@ -159,6 +172,7 @@ The API is now being served at `https://localhost:5052`.
To test connectivity, you can run the following: To test connectivity, you can run the following:
```bash ```bash
curl -X GET "https://localhost:5052/eth/v1/node/version" -H "accept: application/json" --cacert cert.pem | jq curl -X GET "https://localhost:5052/eth/v1/node/version" -H "accept: application/json" --cacert cert.pem | jq
``` ```
### Connecting a validator client ### Connecting a validator client
In order to connect a validator client to a beacon node over TLS, the validator In order to connect a validator client to a beacon node over TLS, the validator
@ -201,13 +215,13 @@ Ensure the `--http` flag has been supplied at the CLI.
You can quickly check that the HTTP endpoint is up using `curl`: You can quickly check that the HTTP endpoint is up using `curl`:
```bash ```bash
curl -X GET "http://localhost:5052/eth/v1/node/version" -H "accept: application/json" | jq curl -X GET "http://localhost:5052/eth/v1/node/version" -H "accept:application/json"
``` ```
The beacon node should respond with its version: The beacon node should respond with its version:
```json ```json
{"data":{"version":"Lighthouse/v0.2.9-6f7b4768a/x86_64-linux"}} {"data":{"version":"Lighthouse/v4.1.0-693886b/x86_64-linux"}
``` ```
If this doesn't work, the server might not be started or there might be a If this doesn't work, the server might not be started or there might be a

View File

@ -1,8 +1,8 @@
# Lighthouse Non-Standard APIs # Lighthouse Non-Standard APIs
Lighthouse fully supports the standardization efforts at Lighthouse fully supports the standardization efforts at
[github.com/ethereum/beacon-APIs](https://github.com/ethereum/beacon-APIs), [github.com/ethereum/beacon-APIs](https://github.com/ethereum/beacon-APIs).
however sometimes development requires additional endpoints that shouldn't However, sometimes development requires additional endpoints that shouldn't
necessarily be defined as a broad-reaching standard. Such endpoints are placed necessarily be defined as a broad-reaching standard. Such endpoints are placed
behind the `/lighthouse` path. behind the `/lighthouse` path.
@ -16,10 +16,12 @@ Although we don't recommend that users rely on these endpoints, we
document them briefly so they can be utilized by developers and document them briefly so they can be utilized by developers and
researchers. researchers.
### `/lighthouse/health` ### `/lighthouse/health`
*Note: This endpoint is presently only available on Linux.*
*Presently only available on Linux.* Returns information regarding the health of the host machine.
```bash ```bash
curl -X GET "http://localhost:5052/lighthouse/health" -H "accept: application/json" | jq curl -X GET "http://localhost:5052/lighthouse/health" -H "accept: application/json" | jq
``` ```
@ -63,7 +65,7 @@ curl -X GET "http://localhost:5052/lighthouse/health" -H "accept: application/j
``` ```
### `/lighthouse/ui/health` ### `/lighthouse/ui/health`
Returns information regarding the health of the host machine.
```bash ```bash
curl -X GET "http://localhost:5052/lighthouse/ui/health" -H "accept: application/json" | jq curl -X GET "http://localhost:5052/lighthouse/ui/health" -H "accept: application/json" | jq
@ -83,24 +85,24 @@ curl -X GET "http://localhost:5052/lighthouse/ui/health" -H "accept: applicatio
"global_cpu_frequency": 3.4, "global_cpu_frequency": 3.4,
"disk_bytes_total": 502390845440, "disk_bytes_total": 502390845440,
"disk_bytes_free": 9981386752, "disk_bytes_free": 9981386752,
"network_name": "wlp0s20f3",
"network_bytes_total_received": 14105556611,
"network_bytes_total_transmit": 3649489389,
"nat_open": true,
"connected_peers": 80,
"sync_state": "Synced",
"system_uptime": 660706, "system_uptime": 660706,
"app_uptime": 105, "app_uptime": 105,
"system_name": "Arch Linux", "system_name": "Arch Linux",
"kernel_version": "5.19.13-arch1-1", "kernel_version": "5.19.13-arch1-1",
"os_version": "Linux rolling Arch Linux", "os_version": "Linux rolling Arch Linux",
"host_name": "Computer1" "host_name": "Computer1"
"network_name": "wlp0s20f3",
"network_bytes_total_received": 14105556611,
"network_bytes_total_transmit": 3649489389,
"nat_open": true,
"connected_peers": 80,
"sync_state": "Synced",
} }
} }
``` ```
### `/lighthouse/ui/validator_count` ### `/lighthouse/ui/validator_count`
Returns an overview of validators.
```bash ```bash
curl -X GET "http://localhost:5052/lighthouse/ui/validator_count" -H "accept: application/json" | jq curl -X GET "http://localhost:5052/lighthouse/ui/validator_count" -H "accept: application/json" | jq
``` ```
@ -121,9 +123,9 @@ curl -X GET "http://localhost:5052/lighthouse/ui/validator_count" -H "accept: ap
} }
``` ```
### `/lighthouse/ui/validator_metrics` ### `/lighthouse/ui/validator_metrics`
Re-exposes certain metrics from the validator monitor to the HTTP API. Re-exposes certain metrics from the validator monitor to the HTTP API. This API requires that the beacon node to have the flag `--validator-monitor-auto`. This API will only return metrics for the validators currently being monitored and present in the POST data, or the validators running in the validator client.
Will only return metrics for the validators currently being monitored and are present in the POST data.
```bash ```bash
curl -X POST "http://localhost:5052/lighthouse/ui/validator_metrics" -d '{"indices": [12345]}' -H "Content-Type: application/json" | jq curl -X POST "http://localhost:5052/lighthouse/ui/validator_metrics" -d '{"indices": [12345]}' -H "Content-Type: application/json" | jq
``` ```
@ -148,24 +150,40 @@ curl -X POST "http://localhost:5052/lighthouse/ui/validator_metrics" -d '{"indic
} }
} }
``` ```
Running this API without the flag `--validator-monitor-auto` in the beacon node will return null:
```json
{
"data": {
"validators": {}
}
}
```
### `/lighthouse/syncing` ### `/lighthouse/syncing`
Returns the sync status of the beacon node.
```bash ```bash
curl -X GET "http://localhost:5052/lighthouse/syncing" -H "accept: application/json" | jq curl -X GET "http://localhost:5052/lighthouse/syncing" -H "accept: application/json" | jq
``` ```
```json There are two possible outcomes, depending on whether the beacon node is syncing or synced.
{
1. Syncing:
```json
{
"data": { "data": {
"SyncingFinalized": { "SyncingFinalized": {
"start_slot": 3104, "start_slot": "5478848",
"head_slot": 343744, "target_slot": "5478944"
"head_root": "0x1b434b5ed702338df53eb5e3e24336a90373bb51f74b83af42840be7421dd2bf"
} }
} }
} }
``` ```
1. Synced:
```json
{
"data": "Synced"
}
```
### `/lighthouse/peers` ### `/lighthouse/peers`
@ -173,97 +191,138 @@ curl -X GET "http://localhost:5052/lighthouse/syncing" -H "accept: application/
curl -X GET "http://localhost:5052/lighthouse/peers" -H "accept: application/json" | jq curl -X GET "http://localhost:5052/lighthouse/peers" -H "accept: application/json" | jq
``` ```
```json ```json
[ [
{ {
"peer_id": "16Uiu2HAmA9xa11dtNv2z5fFbgF9hER3yq35qYNTPvN7TdAmvjqqv", "peer_id": "16Uiu2HAm2ZoWQ2zkzsMFvf5o7nXa7R5F7H1WzZn2w7biU3afhgov",
"peer_info": { "peer_info": {
"_status": "Healthy",
"score": { "score": {
"score": 0 "Real": {
"lighthouse_score": 0,
"gossipsub_score": -18371.409037358582,
"ignore_negative_gossipsub_score": false,
"score": -21.816048231863316
}
}, },
"client": { "client": {
"kind": "Lighthouse", "kind": "Lighthouse",
"version": "v0.2.9-1c9a055c", "version": "v4.1.0-693886b",
"os_version": "aarch64-linux", "os_version": "x86_64-linux",
"protocol_version": "lighthouse/libp2p", "protocol_version": "eth2/1.0.0",
"agent_string": "Lighthouse/v0.2.9-1c9a055c/aarch64-linux" "agent_string": "Lighthouse/v4.1.0-693886b/x86_64-linux"
}, },
"connection_status": { "connection_status": {
"status": "disconnected", "status": "disconnected",
"connections_in": 0, "connections_in": 0,
"connections_out": 0, "connections_out": 0,
"last_seen": 1082, "last_seen": 9028,
"banned_ips": [] "banned_ips": []
}, },
"listening_addresses": [ "listening_addresses": [
"/ip4/80.109.35.174/tcp/9000", "/ip4/212.102.59.173/tcp/23452",
"/ip4/127.0.0.1/tcp/9000", "/ip4/23.124.84.197/tcp/23452",
"/ip4/192.168.0.73/tcp/9000", "/ip4/127.0.0.1/tcp/23452",
"/ip4/172.17.0.1/tcp/9000", "/ip4/192.168.0.2/tcp/23452",
"/ip6/::1/tcp/9000" "/ip4/192.168.122.1/tcp/23452"
],
"seen_addresses": [
"23.124.84.197:23452"
], ],
"sync_status": { "sync_status": {
"Advanced": { "Synced": {
"info": { "info": {
"status_head_slot": 343829, "head_slot": "5468141",
"status_head_root": "0xe34e43efc2bb462d9f364bc90e1f7f0094e74310fd172af698b5a94193498871", "head_root": "0x7acc017a199c0cf0693a19e0ed3a445a02165c03ea6f46cb5ffb8f60bf0ebf35",
"status_finalized_epoch": 10742, "finalized_epoch": "170877",
"status_finalized_root": "0x1b434b5ed702338df53eb5e3e24336a90373bb51f74b83af42840be7421dd2bf" "finalized_root": "0xbbc3541637976bd03b526de73e60a064e452a4b873b65f43fa91fefbba140410"
} }
} }
}, },
"meta_data": { "meta_data": {
"seq_number": 160, "V2": {
"attnets": "0x0000000800000080" "seq_number": 501,
"attnets": "0x0000020000000000",
"syncnets": "0x00"
} }
},
"subnets": [],
"is_trusted": false,
"connection_direction": "Outgoing",
"enr": "enr:-L64QI37ReMIki2Uqln3pcgQyAH8Y3ceSYrtJp1FlDEGSM37F7ngCpS9k-SKQ1bOHp0zFCkNxpvFlf_3o5OUkBRw0qyCAfqHYXR0bmV0c4gAAAIAAAAAAIRldGgykGKJQe8DABAg__________-CaWSCdjSCaXCEF3xUxYlzZWNwMjU2azGhAmoW921eIvf8pJhOvOwuxLSxKnpLY2inE_bUILdlZvhdiHN5bmNuZXRzAIN0Y3CCW5yDdWRwgluc"
} }
} }
] ]
``` ```
### `/lighthouse/peers/connected` ### `/lighthouse/peers/connected`
Returns information about connected peers.
```bash ```bash
curl -X GET "http://localhost:5052/lighthouse/peers/connected" -H "accept: application/json" | jq curl -X GET "http://localhost:5052/lighthouse/peers/connected" -H "accept: application/json" | jq
``` ```
```json ```json
[ [
{ {
"peer_id": "16Uiu2HAkzJC5TqDSKuLgVUsV4dWat9Hr8EjNZUb6nzFb61mrfqBv", "peer_id": "16Uiu2HAmCAvpoYE6ABGdQJaW4iufVqNCTJU5AqzyZPB2D9qba7ZU",
"peer_info": { "peer_info": {
"_status": "Healthy",
"score": { "score": {
"Real": {
"lighthouse_score": 0,
"gossipsub_score": 0,
"ignore_negative_gossipsub_score": false,
"score": 0 "score": 0
}
}, },
"client": { "client": {
"kind": "Lighthouse", "kind": "Lighthouse",
"version": "v0.2.8-87181204+", "version": "v3.5.1-319cc61",
"os_version": "x86_64-linux", "os_version": "x86_64-linux",
"protocol_version": "lighthouse/libp2p", "protocol_version": "eth2/1.0.0",
"agent_string": "Lighthouse/v0.2.8-87181204+/x86_64-linux" "agent_string": "Lighthouse/v3.5.1-319cc61/x86_64-linux"
}, },
"connection_status": { "connection_status": {
"status": "connected", "status": "connected",
"connections_in": 1, "connections_in": 0,
"connections_out": 0, "connections_out": 1,
"last_seen": 0, "last_seen": 0
"banned_ips": []
}, },
"listening_addresses": [ "listening_addresses": [
"/ip4/34.204.178.218/tcp/9000", "/ip4/144.91.92.17/tcp/9000",
"/ip4/127.0.0.1/tcp/9000", "/ip4/127.0.0.1/tcp/9000",
"/ip4/172.31.67.58/tcp/9000", "/ip4/172.19.0.3/tcp/9000"
"/ip4/172.17.0.1/tcp/9000",
"/ip6/::1/tcp/9000"
], ],
"sync_status": "Unknown", "seen_addresses": [
"meta_data": { "144.91.92.17:9000"
"seq_number": 1819, ],
"attnets": "0xffffffffffffffff" "sync_status": {
"Synced": {
"info": {
"head_slot": "5468930",
"head_root": "0x25409073c65d2f6f5cee20ac2eff5ab980b576ca7053111456063f8ff8f67474",
"finalized_epoch": "170902",
"finalized_root": "0xab59473289e2f708341d8e5aafd544dd88e09d56015c90550ea8d16c50b4436f"
} }
} }
},
"meta_data": {
"V2": {
"seq_number": 67,
"attnets": "0x0000000080000000",
"syncnets": "0x00"
}
},
"subnets": [
{
"Attestation": "39"
}
],
"is_trusted": false,
"connection_direction": "Outgoing",
"enr": "enr:-Ly4QHd3RHJdkuR1iE6MtVtibC5S-aiWGPbwi4cG3wFGbqxRAkAgLDseTzPFQQIehQ7LmO7KIAZ5R1fotjMQ_LjA8n1Dh2F0dG5ldHOIAAAAAAAQAACEZXRoMpBiiUHvAwAQIP__________gmlkgnY0gmlwhJBbXBGJc2VjcDI1NmsxoQL4z8A7B-NS29zOgvkTX1YafKandwOtrqQ1XRnUJj3se4hzeW5jbmV0cwCDdGNwgiMog3VkcIIjKA"
}
} }
] ]
``` ```
@ -297,7 +356,8 @@ health of the execution node that the beacon node is connected to.
- `latest_cached_block_number` & `latest_cached_block_timestamp`: the block - `latest_cached_block_number` & `latest_cached_block_timestamp`: the block
number and timestamp of the latest block we have in our block cache. number and timestamp of the latest block we have in our block cache.
- For correct execution client voting this timestamp should be later than the - For correct execution client voting this timestamp should be later than the
`voting_period_start_timestamp`. `voting_target_timestamp`.
- `voting_target_timestamp`: The latest timestamp allowed for an execution layer block in this voting period. - `voting_target_timestamp`: The latest timestamp allowed for an execution layer block in this voting period.
- `eth1_node_sync_status_percentage` (float): An estimate of how far the head of the - `eth1_node_sync_status_percentage` (float): An estimate of how far the head of the
execution node is from the head of the execution chain. execution node is from the head of the execution chain.
@ -422,9 +482,9 @@ curl -X GET "http://localhost:5052/lighthouse/beacon/states/0/ssz" | jq
POST request that checks if any of the given validators have attested in the given epoch. Returns a list POST request that checks if any of the given validators have attested in the given epoch. Returns a list
of objects, each including the validator index, epoch, and `is_live` status of a requested validator. of objects, each including the validator index, epoch, and `is_live` status of a requested validator.
This endpoint is used in doppelganger detection, and will only provide accurate information for the This endpoint is used in doppelganger detection, and can only provide accurate information for the current, previous, or next epoch.
current, previous, or next epoch.
> Note that for this API, if you insert an arbitrary epoch other than the previous, current or next epoch of the network, it will return `"code:400"` and `BAD_REQUEST`.
```bash ```bash
curl -X POST "http://localhost:5052/lighthouse/liveness" -d '{"indices":["0","1"],"epoch":"1"}' -H "content-type: application/json" | jq curl -X POST "http://localhost:5052/lighthouse/liveness" -d '{"indices":["0","1"],"epoch":"1"}' -H "content-type: application/json" | jq
@ -442,6 +502,8 @@ curl -X POST "http://localhost:5052/lighthouse/liveness" -d '{"indices":["0","1"
} }
``` ```
### `/lighthouse/database/info` ### `/lighthouse/database/info`
Information about the database's split point and anchor info. Information about the database's split point and anchor info.
@ -450,26 +512,29 @@ Information about the database's split point and anchor info.
curl "http://localhost:5052/lighthouse/database/info" | jq curl "http://localhost:5052/lighthouse/database/info" | jq
``` ```
```json ```json
{ {
"schema_version": 5, "schema_version": 16,
"config": { "config": {
"slots_per_restore_point": 2048, "slots_per_restore_point": 8192,
"slots_per_restore_point_set_explicitly": false,
"block_cache_size": 5, "block_cache_size": 5,
"historic_state_cache_size": 1, "historic_state_cache_size": 1,
"compact_on_init": false, "compact_on_init": false,
"compact_on_prune": true "compact_on_prune": true,
"prune_payloads": true
}, },
"split": { "split": {
"slot": "2034912", "slot": "5485952",
"state_root": "0x11c8516aa7d4d1613e84121e3a557ceca34618b4c1a38f05b66ad045ff82b33b" "state_root": "0xcfe5d41e6ab5a9dab0de00d89d97ae55ecaeed3b08e4acda836e69b2bef698b4"
}, },
"anchor": { "anchor": {
"anchor_slot": "2034720", "anchor_slot": "5414688",
"oldest_block_slot": "1958881", "oldest_block_slot": "0",
"oldest_block_parent": "0x1fd3d855d03e9df28d8a41a0f9cb9d4c540832b3ca1c3e1d7e09cd75b874cc87", "oldest_block_parent": "0x0000000000000000000000000000000000000000000000000000000000000000",
"state_upper_limit": "2035712", "state_upper_limit": "5414912",
"state_lower_limit": "0" "state_lower_limit": "8192"
} }
} }
``` ```
@ -504,12 +569,12 @@ Manually provide `SignedBeaconBlock`s to backfill the database. This is intended
for use by Lighthouse developers during testing only. for use by Lighthouse developers during testing only.
### `/lighthouse/merge_readiness` ### `/lighthouse/merge_readiness`
Returns the current difficulty and terminal total difficulty of the network. Before [The Merge](https://ethereum.org/en/roadmap/merge/) on 15<sup>th</sup> September 2022, you will see that the current difficulty is less than the terminal total difficulty, An example is shown below:
```bash ```bash
curl -X GET "http://localhost:5052/lighthouse/merge_readiness" | jq curl -X GET "http://localhost:5052/lighthouse/merge_readiness" | jq
``` ```
``` ```json
{ {
"data":{ "data":{
"type":"ready", "type":"ready",
@ -521,6 +586,21 @@ curl -X GET "http://localhost:5052/lighthouse/merge_readiness" | jq
} }
``` ```
As all testnets and Mainnet have been merged, both values will be the same after The Merge. An example of response on the Goerli testnet:
```json
{
"data": {
"type": "ready",
"config": {
"terminal_total_difficulty": "10790000"
},
"current_difficulty": "10790000"
}
}
```
### `/lighthouse/analysis/attestation_performance/{index}` ### `/lighthouse/analysis/attestation_performance/{index}`
Fetch information about the attestation performance of a validator index or all validators for a Fetch information about the attestation performance of a validator index or all validators for a
@ -611,19 +691,34 @@ Two query parameters are required:
Example: Example:
```bash ```bash
curl -X GET "http://localhost:5052/lighthouse/analysis/block_rewards?start_slot=1&end_slot=32" | jq curl -X GET "http://localhost:5052/lighthouse/analysis/block_rewards?start_slot=1&end_slot=1" | jq
``` ```
The first few lines of the response would look like:
```json ```json
[ [
{ {
"block_root": "0x51576c2fcf0ab68d7d93c65e6828e620efbb391730511ffa35584d6c30e51410", "total": 637260,
"block_root": "0x4a089c5e390bb98e66b27358f157df825128ea953cee9d191229c0bcf423a4f6",
"meta": {
"slot": "1",
"parent_slot": "0",
"proposer_index": 93,
"graffiti": "EF #vm-eth2-raw-iron-prater-101"
},
"attestation_rewards": { "attestation_rewards": {
"total": 4941156, "total": 637260,
}, "prev_epoch_total": 0,
.. "curr_epoch_total": 637260,
}, "per_attestation_rewards": [
.. {
"50102": 780,
}
]
}
}
] ]
``` ```
@ -653,6 +748,8 @@ Two query parameters are required:
curl -X GET "http://localhost:5052/lighthouse/analysis/block_packing_efficiency?start_epoch=1&end_epoch=1" | jq curl -X GET "http://localhost:5052/lighthouse/analysis/block_packing_efficiency?start_epoch=1&end_epoch=1" | jq
``` ```
An excerpt of the response looks like:
```json ```json
[ [
{ {
@ -707,3 +804,16 @@ Should provide an output that emits log events as they occur:
} }
} }
``` ```
### `/lighthouse/nat`
Checks if the ports are open.
```bash
curl -X GET "http://localhost:5052/lighthouse/nat" | jq
```
An open port will return:
```json
{
"data": true
}

View File

@ -17,8 +17,11 @@ HTTP Path | Description |
[`POST /lighthouse/validators/mnemonic`](#post-lighthousevalidatorsmnemonic) | Create a new validator from an existing mnemonic. [`POST /lighthouse/validators/mnemonic`](#post-lighthousevalidatorsmnemonic) | Create a new validator from an existing mnemonic.
[`POST /lighthouse/validators/web3signer`](#post-lighthousevalidatorsweb3signer) | Add web3signer validators. [`POST /lighthouse/validators/web3signer`](#post-lighthousevalidatorsweb3signer) | Add web3signer validators.
The query to Lighthouse API endpoints requires authorization, see [Authorization Header](./api-vc-auth-header.md).
In addition to the above endpoints Lighthouse also supports all of the [standard keymanager APIs](https://ethereum.github.io/keymanager-APIs/). In addition to the above endpoints Lighthouse also supports all of the [standard keymanager APIs](https://ethereum.github.io/keymanager-APIs/).
## `GET /lighthouse/version` ## `GET /lighthouse/version`
Returns the software version and `git` commit hash for the Lighthouse binary. Returns the software version and `git` commit hash for the Lighthouse binary.
@ -32,15 +35,28 @@ Returns the software version and `git` commit hash for the Lighthouse binary.
| Required Headers | [`Authorization`](./api-vc-auth-header.md) | | Required Headers | [`Authorization`](./api-vc-auth-header.md) |
| Typical Responses | 200 | | Typical Responses | 200 |
### Example Response Body Command:
```bash
DATADIR=/var/lib/lighthouse
curl -X GET "http://localhost:5062/lighthouse/version" -H "Authorization: Bearer $(cat ${DATADIR}/validators/api-token.txt)" | jq
```
Example Response Body:
```json ```json
{ {
"data": { "data": {
"version": "Lighthouse/v0.2.11-fc0654fbe+/x86_64-linux" "version": "Lighthouse/v4.1.0-693886b/x86_64-linux"
} }
} }
``` ```
> Note: The command provided in this documentation links to the API token file. In this documentation, it is assumed that the API token file is located in `/var/lib/lighthouse/validators/API-token.txt`. If your database is saved in another directory, modify the `DATADIR` accordingly. If you are having permission issue with accessing the API token file, you can modify the header to become `-H "Authorization: Bearer $(sudo cat ${DATADIR}/validators/api-token.txt)"`.
> As an alternative, you can also provide the API token directly, for example, `-H "Authorization: Bearer api-token-0x02dc2a13115cc8c83baf170f597f22b1eb2930542941ab902df3daadebcb8f8176`. In this case, you obtain the token from the file `API token.txt` and the command becomes:
```bash
curl -X GET "http://localhost:5062/lighthouse/version" -H "Authorization: Bearer api-token-0x02dc2a13115cc8c83baf170f597f22b1eb2930542941ab902df3daadebcb8f8176" | jq
```
## `GET /lighthouse/health` ## `GET /lighthouse/health`
@ -57,23 +73,47 @@ Returns information regarding the health of the host machine.
*Note: this endpoint is presently only available on Linux.* *Note: this endpoint is presently only available on Linux.*
### Example Response Body Command:
```bash
DATADIR=/var/lib/lighthouse
curl -X GET "http://localhost:5062/lighthouse/health" -H "Authorization: Bearer $(cat ${DATADIR}/validators/api-token.txt)" | jq
```
Example Response Body:
```json ```json
{ {
"data": { "data": {
"pid": 1476293, "sys_virt_mem_total": 8184274944,
"pid_num_threads": 19, "sys_virt_mem_available": 1532280832,
"pid_mem_resident_set_size": 4009984, "sys_virt_mem_used": 6248341504,
"pid_mem_virtual_memory_size": 1306775552, "sys_virt_mem_free": 648790016,
"sys_virt_mem_total": 33596100608, "sys_virt_mem_percent": 81.27775,
"sys_virt_mem_available": 23073017856, "sys_virt_mem_cached": 1244770304,
"sys_virt_mem_used": 9346957312, "sys_virt_mem_buffers": 42373120,
"sys_virt_mem_free": 22410510336, "sys_loadavg_1": 2.33,
"sys_virt_mem_percent": 31.322334, "sys_loadavg_5": 2.11,
"sys_loadavg_1": 0.98, "sys_loadavg_15": 2.47,
"sys_loadavg_5": 0.98, "cpu_cores": 4,
"sys_loadavg_15": 1.01 "cpu_threads": 8,
"system_seconds_total": 103095,
"user_seconds_total": 750734,
"iowait_seconds_total": 60671,
"idle_seconds_total": 3922305,
"cpu_time_total": 4794222,
"disk_node_bytes_total": 982820896768,
"disk_node_bytes_free": 521943703552,
"disk_node_reads_total": 376287830,
"disk_node_writes_total": 48232652,
"network_node_bytes_total_received": 143003442144,
"network_node_bytes_total_transmit": 185348289905,
"misc_node_boot_ts_seconds": 1681740973,
"misc_os": "linux",
"pid": 144072,
"pid_num_threads": 27,
"pid_mem_resident_set_size": 15835136,
"pid_mem_virtual_memory_size": 2179018752,
"pid_process_seconds_total": 54
} }
} }
``` ```
@ -91,7 +131,13 @@ Returns information regarding the health of the host machine.
| Required Headers | [`Authorization`](./api-vc-auth-header.md) | | Required Headers | [`Authorization`](./api-vc-auth-header.md) |
| Typical Responses | 200 | | Typical Responses | 200 |
### Example Response Body Command:
```bash
DATADIR=/var/lib/lighthouse
curl -X GET "http://localhost:5062/lighthouse/ui/health" -H "Authorization: Bearer $(cat ${DATADIR}/validators/api-token.txt)" | jq
```
Example Response Body
```json ```json
{ {
@ -130,7 +176,12 @@ Returns the graffiti that will be used for the next block proposal of each valid
| Required Headers | [`Authorization`](./api-vc-auth-header.md) | | Required Headers | [`Authorization`](./api-vc-auth-header.md) |
| Typical Responses | 200 | | Typical Responses | 200 |
### Example Response Body Command:
```bash
DATADIR=/var/lib/lighthouse
curl -X GET "http://localhost:5062/lighthouse/ui/graffiti" -H "Authorization: Bearer $(cat ${DATADIR}/validators/api-token.txt)" | jq
```
Example Response Body
```json ```json
{ {
@ -155,70 +206,114 @@ Returns the Ethereum proof-of-stake consensus specification loaded for this vali
| Required Headers | [`Authorization`](./api-vc-auth-header.md) | | Required Headers | [`Authorization`](./api-vc-auth-header.md) |
| Typical Responses | 200 | | Typical Responses | 200 |
### Example Response Body Command:
```bash
DATADIR=/var/lib/lighthouse
curl -X GET "http://localhost:5062/lighthouse/spec" -H "Authorization: Bearer $(cat ${DATADIR}/validators/api-token.txt)" | jq
```
Example Response Body
```json ```json
{ {
"data": { "data": {
"CONFIG_NAME": "mainnet", "CONFIG_NAME": "prater",
"MAX_COMMITTEES_PER_SLOT": "64", "PRESET_BASE": "mainnet",
"TARGET_COMMITTEE_SIZE": "128", "TERMINAL_TOTAL_DIFFICULTY": "10790000",
"TERMINAL_BLOCK_HASH": "0x0000000000000000000000000000000000000000000000000000000000000000",
"TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH": "18446744073709551615",
"SAFE_SLOTS_TO_IMPORT_OPTIMISTICALLY": "128",
"MIN_GENESIS_ACTIVE_VALIDATOR_COUNT": "16384",
"MIN_GENESIS_TIME": "1614588812",
"GENESIS_FORK_VERSION": "0x00001020",
"GENESIS_DELAY": "1919188",
"ALTAIR_FORK_VERSION": "0x01001020",
"ALTAIR_FORK_EPOCH": "36660",
"BELLATRIX_FORK_VERSION": "0x02001020",
"BELLATRIX_FORK_EPOCH": "112260",
"CAPELLA_FORK_VERSION": "0x03001020",
"CAPELLA_FORK_EPOCH": "162304",
"SECONDS_PER_SLOT": "12",
"SECONDS_PER_ETH1_BLOCK": "14",
"MIN_VALIDATOR_WITHDRAWABILITY_DELAY": "256",
"SHARD_COMMITTEE_PERIOD": "256",
"ETH1_FOLLOW_DISTANCE": "2048",
"INACTIVITY_SCORE_BIAS": "4",
"INACTIVITY_SCORE_RECOVERY_RATE": "16",
"EJECTION_BALANCE": "16000000000",
"MIN_PER_EPOCH_CHURN_LIMIT": "4", "MIN_PER_EPOCH_CHURN_LIMIT": "4",
"CHURN_LIMIT_QUOTIENT": "65536", "CHURN_LIMIT_QUOTIENT": "65536",
"PROPOSER_SCORE_BOOST": "40",
"DEPOSIT_CHAIN_ID": "5",
"DEPOSIT_NETWORK_ID": "5",
"DEPOSIT_CONTRACT_ADDRESS": "0xff50ed3d0ec03ac01d4c79aad74928bff48a7b2b",
"MAX_COMMITTEES_PER_SLOT": "64",
"TARGET_COMMITTEE_SIZE": "128",
"MAX_VALIDATORS_PER_COMMITTEE": "2048",
"SHUFFLE_ROUND_COUNT": "90", "SHUFFLE_ROUND_COUNT": "90",
"MIN_GENESIS_ACTIVE_VALIDATOR_COUNT": "1024",
"MIN_GENESIS_TIME": "1601380800",
"GENESIS_DELAY": "172800",
"MIN_DEPOSIT_AMOUNT": "1000000000",
"MAX_EFFECTIVE_BALANCE": "32000000000",
"EJECTION_BALANCE": "16000000000",
"EFFECTIVE_BALANCE_INCREMENT": "1000000000",
"HYSTERESIS_QUOTIENT": "4", "HYSTERESIS_QUOTIENT": "4",
"HYSTERESIS_DOWNWARD_MULTIPLIER": "1", "HYSTERESIS_DOWNWARD_MULTIPLIER": "1",
"HYSTERESIS_UPWARD_MULTIPLIER": "5", "HYSTERESIS_UPWARD_MULTIPLIER": "5",
"PROPORTIONAL_SLASHING_MULTIPLIER": "3", "SAFE_SLOTS_TO_UPDATE_JUSTIFIED": "8",
"GENESIS_FORK_VERSION": "0x00000002", "MIN_DEPOSIT_AMOUNT": "1000000000",
"BLS_WITHDRAWAL_PREFIX": "0x00", "MAX_EFFECTIVE_BALANCE": "32000000000",
"SECONDS_PER_SLOT": "12", "EFFECTIVE_BALANCE_INCREMENT": "1000000000",
"MIN_ATTESTATION_INCLUSION_DELAY": "1", "MIN_ATTESTATION_INCLUSION_DELAY": "1",
"SLOTS_PER_EPOCH": "32",
"MIN_SEED_LOOKAHEAD": "1", "MIN_SEED_LOOKAHEAD": "1",
"MAX_SEED_LOOKAHEAD": "4", "MAX_SEED_LOOKAHEAD": "4",
"MIN_EPOCHS_TO_INACTIVITY_PENALTY": "4", "EPOCHS_PER_ETH1_VOTING_PERIOD": "64",
"MIN_VALIDATOR_WITHDRAWABILITY_DELAY": "256",
"SHARD_COMMITTEE_PERIOD": "256",
"BASE_REWARD_FACTOR": "64",
"WHISTLEBLOWER_REWARD_QUOTIENT": "512",
"PROPOSER_REWARD_QUOTIENT": "8",
"INACTIVITY_PENALTY_QUOTIENT": "16777216",
"MIN_SLASHING_PENALTY_QUOTIENT": "32",
"SAFE_SLOTS_TO_UPDATE_JUSTIFIED": "8",
"DOMAIN_BEACON_PROPOSER": "0x00000000",
"DOMAIN_BEACON_ATTESTER": "0x01000000",
"DOMAIN_RANDAO": "0x02000000",
"DOMAIN_DEPOSIT": "0x03000000",
"DOMAIN_VOLUNTARY_EXIT": "0x04000000",
"DOMAIN_SELECTION_PROOF": "0x05000000",
"DOMAIN_AGGREGATE_AND_PROOF": "0x06000000",
"DOMAIN_APPLICATION_MASK": "0x00000001",
"MAX_VALIDATORS_PER_COMMITTEE": "2048",
"SLOTS_PER_EPOCH": "32",
"EPOCHS_PER_ETH1_VOTING_PERIOD": "32",
"SLOTS_PER_HISTORICAL_ROOT": "8192", "SLOTS_PER_HISTORICAL_ROOT": "8192",
"MIN_EPOCHS_TO_INACTIVITY_PENALTY": "4",
"EPOCHS_PER_HISTORICAL_VECTOR": "65536", "EPOCHS_PER_HISTORICAL_VECTOR": "65536",
"EPOCHS_PER_SLASHINGS_VECTOR": "8192", "EPOCHS_PER_SLASHINGS_VECTOR": "8192",
"HISTORICAL_ROOTS_LIMIT": "16777216", "HISTORICAL_ROOTS_LIMIT": "16777216",
"VALIDATOR_REGISTRY_LIMIT": "1099511627776", "VALIDATOR_REGISTRY_LIMIT": "1099511627776",
"BASE_REWARD_FACTOR": "64",
"WHISTLEBLOWER_REWARD_QUOTIENT": "512",
"PROPOSER_REWARD_QUOTIENT": "8",
"INACTIVITY_PENALTY_QUOTIENT": "67108864",
"MIN_SLASHING_PENALTY_QUOTIENT": "128",
"PROPORTIONAL_SLASHING_MULTIPLIER": "1",
"MAX_PROPOSER_SLASHINGS": "16", "MAX_PROPOSER_SLASHINGS": "16",
"MAX_ATTESTER_SLASHINGS": "2", "MAX_ATTESTER_SLASHINGS": "2",
"MAX_ATTESTATIONS": "128", "MAX_ATTESTATIONS": "128",
"MAX_DEPOSITS": "16", "MAX_DEPOSITS": "16",
"MAX_VOLUNTARY_EXITS": "16", "MAX_VOLUNTARY_EXITS": "16",
"ETH1_FOLLOW_DISTANCE": "1024", "INACTIVITY_PENALTY_QUOTIENT_ALTAIR": "50331648",
"TARGET_AGGREGATORS_PER_COMMITTEE": "16", "MIN_SLASHING_PENALTY_QUOTIENT_ALTAIR": "64",
"PROPORTIONAL_SLASHING_MULTIPLIER_ALTAIR": "2",
"SYNC_COMMITTEE_SIZE": "512",
"EPOCHS_PER_SYNC_COMMITTEE_PERIOD": "256",
"MIN_SYNC_COMMITTEE_PARTICIPANTS": "1",
"INACTIVITY_PENALTY_QUOTIENT_BELLATRIX": "16777216",
"MIN_SLASHING_PENALTY_QUOTIENT_BELLATRIX": "32",
"PROPORTIONAL_SLASHING_MULTIPLIER_BELLATRIX": "3",
"MAX_BYTES_PER_TRANSACTION": "1073741824",
"MAX_TRANSACTIONS_PER_PAYLOAD": "1048576",
"BYTES_PER_LOGS_BLOOM": "256",
"MAX_EXTRA_DATA_BYTES": "32",
"MAX_BLS_TO_EXECUTION_CHANGES": "16",
"MAX_WITHDRAWALS_PER_PAYLOAD": "16",
"MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP": "16384",
"DOMAIN_DEPOSIT": "0x03000000",
"BLS_WITHDRAWAL_PREFIX": "0x00",
"RANDOM_SUBNETS_PER_VALIDATOR": "1", "RANDOM_SUBNETS_PER_VALIDATOR": "1",
"DOMAIN_SYNC_COMMITTEE": "0x07000000",
"TARGET_AGGREGATORS_PER_SYNC_SUBCOMMITTEE": "16",
"DOMAIN_BEACON_ATTESTER": "0x01000000",
"DOMAIN_VOLUNTARY_EXIT": "0x04000000",
"DOMAIN_SYNC_COMMITTEE_SELECTION_PROOF": "0x08000000",
"DOMAIN_CONTRIBUTION_AND_PROOF": "0x09000000",
"EPOCHS_PER_RANDOM_SUBNET_SUBSCRIPTION": "256", "EPOCHS_PER_RANDOM_SUBNET_SUBSCRIPTION": "256",
"SECONDS_PER_ETH1_BLOCK": "14", "TARGET_AGGREGATORS_PER_COMMITTEE": "16",
"DEPOSIT_CONTRACT_ADDRESS": "0x48b597f4b53c21b48ad95c7256b49d1779bd5890" "DOMAIN_APPLICATION_MASK": "0x00000001",
"DOMAIN_AGGREGATE_AND_PROOF": "0x06000000",
"DOMAIN_RANDAO": "0x02000000",
"DOMAIN_SELECTION_PROOF": "0x05000000",
"DOMAIN_BEACON_PROPOSER": "0x00000000",
"SYNC_COMMITTEE_SUBNET_COUNT": "4"
} }
} }
``` ```
@ -240,13 +335,13 @@ file may be read by a local user with access rights.
| Required Headers | - | | Required Headers | - |
| Typical Responses | 200 | | Typical Responses | 200 |
### Example Path Command:
``` ```bash
localhost:5062/lighthouse/auth curl http://localhost:5062/lighthouse/auth | jq
``` ```
### Example Response Body Example Response Body
```json ```json
{ {
@ -267,7 +362,14 @@ Lists all validators managed by this validator client.
| Required Headers | [`Authorization`](./api-vc-auth-header.md) | | Required Headers | [`Authorization`](./api-vc-auth-header.md) |
| Typical Responses | 200 | | Typical Responses | 200 |
### Example Response Body Command:
```bash
DATADIR=/var/lib/lighthouse
curl -X GET "http://localhost:5062/lighthouse/validators/" -H "Authorization: Bearer $(cat ${DATADIR}/validators/api-token.txt)" | jq
```
Example Response Body
```json ```json
{ {
@ -304,13 +406,14 @@ Get a validator by their `voting_pubkey`.
| Required Headers | [`Authorization`](./api-vc-auth-header.md) | | Required Headers | [`Authorization`](./api-vc-auth-header.md) |
| Typical Responses | 200, 400 | | Typical Responses | 200, 400 |
### Example Path Command:
``` ```bash
localhost:5062/lighthouse/validators/0xb0148e6348264131bf47bcd1829590e870c836dc893050fd0dadc7a28949f9d0a72f2805d027521b45441101f0cc1cde DATADIR=/var/lib/lighthouse
curl -X GET "http://localhost:5062/lighthouse/validators/0xb0148e6348264131bf47bcd1829590e870c836dc893050fd0dadc7a28949f9d0a72f2805d027521b45441101f0cc1cde" -H "Authorization: Bearer $(cat ${DATADIR}/validators/api-token.txt)" | jq
``` ```
### Example Response Body Example Response Body
```json ```json
{ {
@ -323,7 +426,7 @@ localhost:5062/lighthouse/validators/0xb0148e6348264131bf47bcd1829590e870c836dc8
## `PATCH /lighthouse/validators/:voting_pubkey` ## `PATCH /lighthouse/validators/:voting_pubkey`
Update some values for the validator with `voting_pubkey`. Update some values for the validator with `voting_pubkey`. The following example updates a validator from `enabled: true` to `enabled: false`
### HTTP Specification ### HTTP Specification
@ -334,13 +437,8 @@ Update some values for the validator with `voting_pubkey`.
| Required Headers | [`Authorization`](./api-vc-auth-header.md) | | Required Headers | [`Authorization`](./api-vc-auth-header.md) |
| Typical Responses | 200, 400 | | Typical Responses | 200, 400 |
### Example Path
``` Example Request Body
localhost:5062/lighthouse/validators/0xb0148e6348264131bf47bcd1829590e870c836dc893050fd0dadc7a28949f9d0a72f2805d027521b45441101f0cc1cde
```
### Example Request Body
```json ```json
{ {
@ -348,12 +446,29 @@ localhost:5062/lighthouse/validators/0xb0148e6348264131bf47bcd1829590e870c836dc8
} }
``` ```
Command:
```bash
DATADIR=/var/lib/lighthouse
curl -X PATCH "http://localhost:5062/lighthouse/validators/0xb0148e6348264131bf47bcd1829590e870c836dc893050fd0dadc7a28949f9d0a72f2805d027521b45441101f0cc1cde" \
-H "Authorization: Bearer $(cat ${DATADIR}/validators/api-token.txt)" \
-H "Content-Type: application/json" \
-d "{\"enabled\":false}" | jq
```
### Example Response Body ### Example Response Body
```json ```json
null null
``` ```
A `null` response indicates that the request is successful. At the same time, `lighthouse vc` will log:
```
INFO Disabled validator voting_pubkey: 0xb0148e6348264131bf47bcd1829590e870c836dc893050fd0dadc7a28949f9d0a72f2805d027521b45441101f0cc1cde
INFO Modified key_cache saved successfully
```
## `POST /lighthouse/validators/` ## `POST /lighthouse/validators/`
Create any number of new validators, all of which will share a common mnemonic Create any number of new validators, all of which will share a common mnemonic
@ -392,6 +507,28 @@ Validators are generated from the mnemonic according to
] ]
``` ```
Command:
```bash
DATADIR=/var/lib/lighthouse
curl -X POST http://localhost:5062/lighthouse/validators \
-H "Authorization: Bearer $(cat ${DATADIR}/validators/api-token.txt)" \
-H "Content-Type: application/json" \
-d '[
{
"enable": true,
"description": "validator_one",
"deposit_gwei": "32000000000",
"graffiti": "Mr F was here",
"suggested_fee_recipient": "0xa2e334e71511686bcfe38bb3ee1ad8f6babcc03d"
},
{
"enable": false,
"description": "validator two",
"deposit_gwei": "34000000000"
}
]' | jq
```
### Example Response Body ### Example Response Body
```json ```json
@ -416,6 +553,14 @@ Validators are generated from the mnemonic according to
] ]
} }
} }
```
`lighthouse vc` will log:
```
INFO Enabled validator voting_pubkey: 0x8ffbc881fb60841a4546b4b385ec5e9b5090fd1c4395e568d98b74b94b41a912c6101113da39d43c101369eeb9b48e50, signing_method: local_keystore
INFO Modified key_cache saved successfully
INFO Disabled validator voting_pubkey: 0xa9fadd620dc68e9fe0d6e1a69f6c54a0271ad65ab5a509e645e45c6e60ff8f4fc538f301781193a08b55821444801502
``` ```
## `POST /lighthouse/validators/keystore` ## `POST /lighthouse/validators/keystore`
@ -474,6 +619,19 @@ Import a keystore into the validator client.
} }
``` ```
We can use [JSON to String Converter](https://jsontostring.com/) so that the above data can be properly presented as a command. The command is as below:
Command:
```bash
DATADIR=/var/lib/lighthouse
curl -X POST http://localhost:5062/lighthouse/validators/keystore \
-H "Authorization: Bearer $(cat ${DATADIR}/validators/api-token.txt)" \
-H "Content-Type: application/json" \
-d "{\"enable\":true,\"password\":\"mypassword\",\"keystore\":{\"crypto\":{\"kdf\":{\"function\":\"scrypt\",\"params\":{\"dklen\":32,\"n\":262144,\"r\":8,\"p\":1,\"salt\":\"445989ec2f332bb6099605b4f1562c0df017488d8d7fb3709f99ebe31da94b49\"},\"message\":\"\"},\"checksum\":{\"function\":\"sha256\",\"params\":{},\"message\":\"abadc1285fd38b24a98ac586bda5b17a8f93fc1ff0778803dc32049578981236\"},\"cipher\":{\"function\":\"aes-128-ctr\",\"params\":{\"iv\":\"65abb7e1d02eec9910d04299cc73efbe\"},\"message\":\"6b7931a4447be727a3bb5dc106d9f3c1ba50671648e522f213651d13450b6417\"}},\"uuid\":\"5cf2a1fb-dcd6-4095-9ebf-7e4ee0204cab\",\"path\":\"m/12381/3600/0/0/0\",\"pubkey\":\"b0d2f05014de27c6d7981e4a920799db1c512ee7922932be6bf55729039147cf35a090bd4ab378fe2d133c36cbbc9969\",\"version\":4,\"description\":\"\"}}" | jq
```
As this is an example for demonstration, the above command will return `InvalidPassword`. However, with a keystore file and correct password, running the above command will import the keystore to the validator client. An example of a success message is shown below:
### Example Response Body ### Example Response Body
```json ```json
{ {
@ -484,6 +642,13 @@ Import a keystore into the validator client.
} }
} }
```
`lighthouse vc` will log:
```bash
INFO Enabled validator voting_pubkey: 0xb0d2f05014de27c6d7981e4a920799db1c512ee7922932be6bf55729039147cf35a090bd4ab378fe2d133c36cbb, signing_method: local_keystore
INFO Modified key_cache saved successfully
``` ```
## `POST /lighthouse/validators/mnemonic` ## `POST /lighthouse/validators/mnemonic`
@ -521,6 +686,16 @@ generated with the path `m/12381/3600/i/42`.
} }
``` ```
Command:
```bash
DATADIR=/var/lib/lighthouse
curl -X POST http://localhost:5062/lighthouse/validators/mnemonic \
-H "Authorization: Bearer $(cat ${DATADIR}/validators/api-token.txt)" \
-H "Content-Type: application/json" \
-d '{"mnemonic":" theme onion deal plastic claim silver fancy youth lock ordinary hotel elegant balance ridge web skill burger survey demand distance legal fish salad cloth","key_derivation_path_offset":0,"validators":[{"enable":true,"description":"validator_one","deposit_gwei":"32000000000"}]}' | jq
```
### Example Response Body ### Example Response Body
```json ```json
@ -537,6 +712,13 @@ generated with the path `m/12381/3600/i/42`.
} }
``` ```
`lighthouse vc` will log:
```
INFO Enabled validator voting_pubkey: 0xa062f95fee747144d5e511940624bc6546509eeaeae9383257a9c43e7ddc58c17c2bab4ae62053122184c381b90db380, signing_method: local_keystore
INFO Modified key_cache saved successfully
```
## `POST /lighthouse/validators/web3signer` ## `POST /lighthouse/validators/web3signer`
Create any number of new validators, all of which will refer to a Create any number of new validators, all of which will refer to a
@ -575,9 +757,29 @@ The following fields may be omitted or nullified to obtain default values:
- `root_certificate_path` - `root_certificate_path`
- `request_timeout_ms` - `request_timeout_ms`
Command:
```bash
DATADIR=/var/lib/lighthouse
curl -X POST http://localhost:5062/lighthouse/validators/web3signer \
-H "Authorization: Bearer $(cat ${DATADIR}/validators/api-token.txt)" \
-H "Content-Type: application/json" \
-d "[{\"enable\":true,\"description\":\"validator_one\",\"graffiti\":\"Mr F was here\",\"suggested_fee_recipient\":\"0xa2e334e71511686bcfe38bb3ee1ad8f6babcc03d\",\"voting_public_key\":\"0xa062f95fee747144d5e511940624bc6546509eeaeae9383257a9c43e7ddc58c17c2bab4ae62053122184c381b90db380\",\"url\":\"http://path-to-web3signer.com\",\"request_timeout_ms\":12000}]"
```
### Example Response Body ### Example Response Body
*No data is included in the response body.*
```json
null
```
A `null` response indicates that the request is successful. At the same time, `lighthouse vc` will log:
```
INFO Enabled validator voting_pubkey: 0xa062f95fee747144d5e511940624bc6546509eeaeae9383257a9c43e7ddc58c17c2bab4ae62053122184c381b90db380, signing_method: remote_signer
```
## `GET /lighthouse/logs` ## `GET /lighthouse/logs`

View File

@ -14,13 +14,13 @@ signers. It also includes some Lighthouse-specific endpoints which are described
## Starting the server ## Starting the server
A Lighthouse validator client can be configured to expose a HTTP server by supplying the `--http` flag. The default listen address is `127.0.0.1:5062`. A Lighthouse validator client can be configured to expose a HTTP server by supplying the `--http` flag. The default listen address is `http://127.0.0.1:5062`.
The following CLI flags control the HTTP server: The following CLI flags control the HTTP server:
- `--http`: enable the HTTP server (required even if the following flags are - `--http`: enable the HTTP server (required even if the following flags are
provided). provided).
- `--http-address`: specify the listen address of the server. It is almost always unsafe to use a non-default HTTP listen address. Use with caution. See the **Security** section below for more information. - `--http-address`: specify the listen address of the server. It is almost always unsafe to use a non-default HTTP listen address. Use this with caution. See the **Security** section below for more information.
- `--http-port`: specify the listen port of the server. - `--http-port`: specify the listen port of the server.
- `--http-allow-origin`: specify the value of the `Access-Control-Allow-Origin` - `--http-allow-origin`: specify the value of the `Access-Control-Allow-Origin`
header. The default is to not supply a header. header. The default is to not supply a header.
@ -28,7 +28,7 @@ The following CLI flags control the HTTP server:
## Security ## Security
The validator client HTTP server is **not encrypted** (i.e., it is **not HTTPS**). For The validator client HTTP server is **not encrypted** (i.e., it is **not HTTPS**). For
this reason, it will listen by default on `127.0.0.1`. this reason, it will listen by default on `http://127.0.0.1`.
It is unsafe to expose the validator client to the public Internet without It is unsafe to expose the validator client to the public Internet without
additional transport layer security (e.g., HTTPS via nginx, SSH tunnels, etc.). additional transport layer security (e.g., HTTPS via nginx, SSH tunnels, etc.).

View File

@ -1,4 +1,4 @@
# MEV and Lighthouse # Maximal Extractable Value (MEV)
Lighthouse is able to interact with servers that implement the [builder Lighthouse is able to interact with servers that implement the [builder
API](https://github.com/ethereum/builder-specs), allowing it to produce blocks without having API](https://github.com/ethereum/builder-specs), allowing it to produce blocks without having
@ -103,11 +103,31 @@ Each field is optional.
} }
``` ```
Command:
```bash
DATADIR=/var/lib/lighthouse
curl -X PATCH "http://localhost:5062/lighthouse/validators/0xb0148e6348264131bf47bcd1829590e870c836dc893050fd0dadc7a28949f9d0a72f2805d027521b45441101f0cc1cde" \
-H "Authorization: Bearer $(sudo cat ${DATADIR}/validators/api-token.txt)" \
-H "Content-Type: application/json" \
-d '{
"builder_proposals": true,
"gas_limit": 30000001
}' | jq
```
#### Example Response Body #### Example Response Body
```json ```json
null null
``` ```
A `null` response indicates that the request is successful. At the same time, `lighthouse vc` will show a log which looks like:
```
INFO Published validator registrations to the builder network, count: 3, service: preparation
```
### Fee Recipient ### Fee Recipient
Refer to [suggested fee recipient](suggested-fee-recipient.md) documentation. Refer to [suggested fee recipient](suggested-fee-recipient.md) documentation.
@ -167,9 +187,18 @@ consider using it for the chance of out-sized rewards, this flag may be useful:
The number provided indicates the minimum reward that an external payload must provide the proposer for it to be considered The number provided indicates the minimum reward that an external payload must provide the proposer for it to be considered
for inclusion in a proposal. For example, if you'd only like to use an external payload for a reward of >= 0.25 ETH, you for inclusion in a proposal. For example, if you'd only like to use an external payload for a reward of >= 0.25 ETH, you
would provide your beacon node with `--builder-profit-threshold 250000000000000000`. If it's your turn to propose and the would provide your beacon node with `--builder-profit-threshold 250000000000000000`. If it's your turn to propose and the
most valuable payload offered by builders is only 0.1 ETH, the local execution engine's payload will be used. Currently, most valuable payload offered by builders is only 0.1 ETH, the local execution engine's payload will be used.
this threshold just looks at the value of the external payload. No comparison to the local payload is made, although
this feature will likely be added in the future. Since the [Capella](https://ethereum.org/en/history/#capella) upgrade, a comparison of the external payload and local payload will be made according to the [engine_getPayloadV2](https://github.com/ethereum/execution-apis/blob/main/src/engine/shanghai.md#engine_getpayloadv2) API. The logic is as follows:
```
if local payload value >= builder payload value:
use local payload
else if builder payload value >= builder_profit_threshold or builder_profit_threshold == 0:
use builder payload
else:
use local payload
```
## Checking your builder config ## Checking your builder config

View File

@ -34,7 +34,7 @@ INFO Loaded checkpoint block and state state_root: 0xe8252c68784a8d5cc7e54
``` ```
> **Security Note**: You should cross-reference the `block_root` and `slot` of the loaded checkpoint > **Security Note**: You should cross-reference the `block_root` and `slot` of the loaded checkpoint
> against a trusted source like a friend's node, or a block explorer. > against a trusted source like a friend's node, a block explorer or some [public endpoints](https://eth-clients.github.io/checkpoint-sync-endpoints/).
Once the checkpoint is loaded Lighthouse will sync forwards to the head of the chain. Once the checkpoint is loaded Lighthouse will sync forwards to the head of the chain.
@ -62,6 +62,10 @@ INFO Downloading historical blocks est_time: 5 hrs 0 mins, speed: 111.96 slots/
Once backfill is complete, a `INFO Historical block download complete` log will be emitted. Once backfill is complete, a `INFO Historical block download complete` log will be emitted.
> Note: Since [v4.1.0](https://github.com/sigp/lighthouse/releases/tag/v4.1.0), Lighthouse implements rate-limited backfilling to mitigate validator performance issues after a recent checkpoint sync. This means that the speed at which historical blocks are downloaded is limited, typically to less than 20 slots/sec. This will not affect validator performance. However, if you would still prefer to sync the chain as fast as possible, you can add the flag `--disable-backfill-rate-limiting` to the beacon node.
> Note: Since [v4.2.0](https://github.com/sigp/lighthouse/releases/tag/v4.2.0), Lighthouse limits the backfill sync to only sync backwards to the weak subjectivity point (approximately 5 months). This will help to save disk space. However, if you would like to sync back to the genesis, you can add the flag `--genesis-backfill` to the beacon node.
## FAQ ## FAQ
1. What if I have an existing database? How can I use checkpoint sync? 1. What if I have an existing database? How can I use checkpoint sync?

View File

@ -29,6 +29,7 @@ validator client or the slasher**.
| v3.4.0 | Jan 2023 | v13 | yes | | v3.4.0 | Jan 2023 | v13 | yes |
| v3.5.0 | Feb 2023 | v15 | yes before Capella | | v3.5.0 | Feb 2023 | v15 | yes before Capella |
| v4.0.1 | Mar 2023 | v16 | yes before Capella | | v4.0.1 | Mar 2023 | v16 | yes before Capella |
| v4.2.0 | May 2023 | v17 | yes |
> **Note**: All point releases (e.g. v2.3.1) are schema-compatible with the prior minor release > **Note**: All point releases (e.g. v2.3.1) are schema-compatible with the prior minor release
> (e.g. v2.3.0). > (e.g. v2.3.0).
@ -82,24 +83,36 @@ on downgrades above.
To check the schema version of a running Lighthouse instance you can use the HTTP API: To check the schema version of a running Lighthouse instance you can use the HTTP API:
```bash ```bash
curl "http://localhost:5052/lighthouse/database/info" curl "http://localhost:5052/lighthouse/database/info" | jq
``` ```
```json ```json
{ {
"schema_version": 8, "schema_version": 16,
"config": { "config": {
"slots_per_restore_point": 8192, "slots_per_restore_point": 8192,
"slots_per_restore_point_set_explicitly": true, "slots_per_restore_point_set_explicitly": false,
"block_cache_size": 5, "block_cache_size": 5,
"historic_state_cache_size": 1, "historic_state_cache_size": 1,
"compact_on_init": false, "compact_on_init": false,
"compact_on_prune": true "compact_on_prune": true,
"prune_payloads": true
},
"split": {
"slot": "5485952",
"state_root": "0xcfe5d41e6ab5a9dab0de00d89d97ae55ecaeed3b08e4acda836e69b2bef698b4"
},
"anchor": {
"anchor_slot": "5414688",
"oldest_block_slot": "0",
"oldest_block_parent": "0x0000000000000000000000000000000000000000000000000000000000000000",
"state_upper_limit": "5414912",
"state_lower_limit": "8192"
} }
} }
``` ```
The `schema_version` key indicates that this database is using schema version 8. The `schema_version` key indicates that this database is using schema version 16.
Alternatively, you can check the schema version with the `lighthouse db` command. Alternatively, you can check the schema version with the `lighthouse db` command.
@ -118,7 +131,7 @@ Several conditions need to be met in order to run `lighthouse db`:
2. The command must run as the user that owns the beacon node database. If you are using systemd then 2. The command must run as the user that owns the beacon node database. If you are using systemd then
your beacon node might run as a user called `lighthousebeacon`. your beacon node might run as a user called `lighthousebeacon`.
3. The `--datadir` flag must be set to the location of the Lighthouse data directory. 3. The `--datadir` flag must be set to the location of the Lighthouse data directory.
4. The `--network` flag must be set to the correct network, e.g. `mainnet`, `prater` or `sepolia`. 4. The `--network` flag must be set to the correct network, e.g. `mainnet`, `goerli` or `sepolia`.
The general form for a `lighthouse db` command is: The general form for a `lighthouse db` command is:

View File

@ -1,29 +1,184 @@
# Frequently Asked Questions # Frequently Asked Questions
- [Why does it take so long for a validator to be activated?](#why-does-it-take-so-long-for-a-validator-to-be-activated) ## [Beacon Node](#beacon-node-1)
- [Do I need to set up any port mappings?](#do-i-need-to-set-up-any-port-mappings) - [I see a warning about "Syncing deposit contract block cache" or an error about "updating deposit contract cache", what should I do?](#bn-deposit-contract)
- [I have a low peer count and it is not increasing](#i-have-a-low-peer-count-and-it-is-not-increasing) - [I see beacon logs showing `WARN: Execution engine called failed`, what should I do?](#bn-ee)
- [What should I do if I lose my slashing protection database?](#what-should-i-do-if-i-lose-my-slashing-protection-database) - [My beacon node is stuck at downloading historical block using checkpoint sync. What should I do?](#bn-download-historical)
- [How do I update lighthouse?](#how-do-i-update-lighthouse) - [I proposed a block but the beacon node shows `could not publish message` with error `duplicate` as below, should I be worried?](#bn-duplicate)
- [I can't compile lighthouse](#i-cant-compile-lighthouse) - [I see beacon node logs `Head is optimistic` and I am missing attestations. What should I do?](#bn-optimistic)
- [What is "Syncing deposit contract block cache"?](#what-is-syncing-deposit-contract-block-cache) - [My beacon node logs `CRIT Beacon block processing error error: ValidatorPubkeyCacheLockTimeout`, what should I do?](#bn-timeout)
- [Can I use redundancy in my staking setup?](#can-i-use-redundancy-in-my-staking-setup) - [My beacon node logs `WARN BlockProcessingFailure outcome: MissingBeaconBlock`, what should I do?](#bn-missing-beacon)
- [How can I monitor my validators?](#how-can-i-monitor-my-validators) - [After checkpoint sync, the progress of `downloading historical blocks` is slow. Why?](#bn-download-slow)
- [I see beacon logs showing `WARN: Execution engine called failed`, what should I do?](#i-see-beacon-logs-showing-warn-execution-engine-called-failed-what-should-i-do) - [My beacon node logs `WARN Error processing HTTP API request`, what should I do?](#bn-http)
- [How do I check or update my withdrawal credentials?](#how-do-i-check-or-update-my-withdrawal-credentials)
- [I am missing attestations. Why?](#i-am-missing-attestations-why)
- [Sometimes I miss the attestation head vote, resulting in penalty. Is this normal?](#sometimes-i-miss-the-attestation-head-vote-resulting-in-penalty-is-this-normal)
- [My beacon node is stuck at downloading historical block using checkpoing sync. What can I do?](#my-beacon-node-is-stuck-at-downloading-historical-block-using-checkpoing-sync-what-can-i-do)
### Why does it take so long for a validator to be activated? ## [Validator](#validator-1)
- [Why does it take so long for a validator to be activated?](#vc-activation)
- [Can I use redundancy in my staking setup?](#vc-redundancy)
- [I am missing attestations. Why?](#vc-missed-attestations)
- [Sometimes I miss the attestation head vote, resulting in penalty. Is this normal?](#vc-head-vote)
- [Can I submit a voluntary exit message without a beacon node?](#vc-exit)
- [Does increasing the number of validators increase the CPU and other computer resources used?](#vc-resource)
- [I want to add new validators. Do I have to reimport the existing keys?](#vc-reimport)
- [Do I have to stop `lighthouse vc` the when importing new validator keys?](#vc-import)
## [Network, Monitoring and Maintenance](#network-monitoring-and-maintenance-1)
- [I have a low peer count and it is not increasing](#net-peer)
- [How do I update lighthouse?](#net-update)
- [Do I need to set up any port mappings (port forwarding)?](#net-port)
- [How can I monitor my validators?](#net-monitor)
- [My beacon node and validator client are on different servers. How can I point the validator client to the beacon node?](#net-bn-vc)
- [Should I do anything to the beacon node or validator client settings if I have a relocation of the node / change of IP address?](#net-ip)
## [Miscellaneous](#miscellaneous-1)
- [What should I do if I lose my slashing protection database?](#misc-slashing)
- [I can't compile lighthouse](#misc-compile)
- [How do I check the version of Lighthouse that is running?](#misc-version)
- [Does Lighthouse have pruning function like the execution client to save disk space?](#misc-prune)
- [Can I use a HDD for the freezer database and only have the hot db on SSD?](#misc-freezer)
## Beacon Node
### <a name="bn-deposit-contract"></a> I see a warning about "Syncing deposit contract block cache" or an error about "updating deposit contract cache", what should I do?
The error can be a warning:
```
Nov 30 21:04:28.268 WARN Syncing deposit contract block cache est_blocks_remaining: initializing deposits, service: slot_notifier
```
or an error:
```
ERRO Error updating deposit contract cache error: Failed to get remote head and new block ranges: EndpointError(FarBehind), retry_millis: 60000, service: deposit_contract_rpc
```
This log indicates that your beacon node is downloading blocks and deposits
from your execution node. When the `est_blocks_remaining` is
`initializing_deposits`, your node is downloading deposit logs. It may stay in
this stage for several minutes. Once the deposits logs are finished
downloading, the `est_blocks_remaining` value will start decreasing.
It is perfectly normal to see this log when starting a node for the first time
or after being off for more than several minutes.
If this log continues appearing during operation, it means your execution client is still syncing and it cannot provide Lighthouse the information about the deposit contract yet. What you need to do is to make sure that the execution client is up and syncing. Once the execution client is synced, the error will disappear.
### <a name="bn-ee"></a> I see beacon logs showing `WARN: Execution engine called failed`, what should I do?
The `WARN Execution engine called failed` log is shown when the beacon node cannot reach the execution engine. When this warning occurs, it will be followed by a detailed message. A frequently encountered example of the error message is:
`error: Reqwest(reqwest::Error { kind: Request, url: Url { scheme: "http", cannot_be_a_base: false, username: "", password: None, host: Some(Ipv4(127.0.0.1)), port: Some(8551), path: "/", query: None, fragment: None }, source: TimedOut }), service: exec`
which says `TimedOut` at the end of the message. This means that the execution engine has not responded in time to the beacon node. One option is to add the flag `--execution-timeout-multiplier 3` to the beacon node. However, if the error persists, it is worth digging further to find out the cause. There are a few reasons why this can occur:
1. The execution engine is not synced. Check the log of the execution engine to make sure that it is synced. If it is syncing, wait until it is synced and the error will disappear. You will see the beacon node logs `INFO Execution engine online` when it is synced.
1. The computer is overloaded. Check the CPU and RAM usage to see if it has overloaded. You can use `htop` to check for CPU and RAM usage.
1. Your SSD is slow. Check if your SSD is in "The Bad" list [here](https://gist.github.com/yorickdowne/f3a3e79a573bf35767cd002cc977b038). If your SSD is in "The Bad" list, it means it cannot keep in sync to the network and you may want to consider upgrading to a better SSD.
If the reason for the error message is caused by no. 1 above, you may want to look further. If the execution engine is out of sync suddenly, it is usually caused by ungraceful shutdown. The common causes for ungraceful shutdown are:
- Power outage. If power outages are an issue at your place, consider getting a UPS to avoid ungraceful shutdown of services.
- The service file is not stopped properly. To overcome this, make sure that the process is stopped properly, e.g., during client updates.
- Out of memory (oom) error. This can happen when the system memory usage has reached its maximum and causes the execution engine to be killed. When this occurs, the log file will show `Main process exited, code=killed, status=9/KILL`. You can also run `sudo journalctl -a --since "18 hours ago" | grep -i "killed process` to confirm that the execution client has been killed due to oom. If you are using geth as the execution client, a short term solution is to reduce the resources used. For example, you can reduce the cache by adding the flag `--cache 2048`. If the oom occurs rather frequently, a long term solution is to increase the memory capacity of the computer.
### <a name="bn-download-historical"></a> My beacon node is stuck at downloading historical block using checkpoint sync. What should I do?
After checkpoint forwards sync completes, the beacon node will start to download historical blocks. The log will look like:
```bash
INFO Downloading historical blocks est_time: --, distance: 4524545 slots (89 weeks 5 days), service: slot_notifier
```
If the same log appears every minute and you do not see progress in downloading historical blocks, you can try one of the followings:
- Check the number of peers you are connected to. If you have low peers (less than 50), try to do port forwarding on the port 9000 TCP/UDP to increase peer count.
- Restart the beacon node.
### <a name="bn-duplicate"></a> I proposed a block but the beacon node shows `could not publish message` with error `duplicate` as below, should I be worried?
```
INFO Block from HTTP API already known`
WARN Could not publish message error: Duplicate, service: libp2p
```
This error usually happens when users are running mev-boost. The relay will publish the block on the network before returning it back to you. After the relay published the block on the network, it will propagate through nodes, and it happens quite often that your node will receive the block from your connected peers via gossip first, before getting the block from the relay, hence the message `duplicate`.
In short, it is nothing to worry about.
### <a name="bn-optimistic"></a> I see beacon node logs `Head is optimistic`, and I am missing attestations. What should I do?
The log looks like:
```
WARN Head is optimistic execution_block_hash: 0x47e7555f1d4215d1ad409b1ac188b008fcb286ed8f38d3a5e8078a0af6cbd6e1, info: chain not fully verified, block and attestation production disabled until execution engine syncs, service: slot_notifier
```
It means the beacon node will follow the chain, but it will not be able to attest or produce blocks. This is because the execution client is not synced, so the beacon chain cannot verify the authenticity of the chain head, hence the word `optimistic`. What you need to do is to make sure that the execution client is up and syncing. Once the execution client is synced, the error will disappear.
### <a name="bn-timeout"></a> My beacon node logs `CRIT Beacon block processing error error: ValidatorPubkeyCacheLockTimeout, service: beacon`, what should I do?
An example of the log is shown below:
```
CRIT Beacon block processing error error: ValidatorPubkeyCacheLockTimeout, service: beacon
WARN BlockProcessingFailure outcome: ValidatorPubkeyCacheLockTimeout, msg: unexpected condition in processing block.
```
A `Timeout` error suggests that the computer may be overloaded at the moment, for example, the execution client is still syncing. You may use the flag `--disable-lock-timeouts` to silence this error, although it will not fix the underlying slowness. Nevertheless, this is a relatively harmless log, and the error should go away once the resources used are back to normal.
### <a name="bn-missing-beacon"></a> My beacon node logs `WARN BlockProcessingFailure outcome: MissingBeaconBlock`, what should I do?
An example of the full log is shown below:
```
WARN BlockProcessingFailure outcome: MissingBeaconBlock(0xbdba211f8d72029554e405d8e4906690dca807d1d7b1bc8c9b88d7970f1648bc), msg: unexpected condition in processing block.
```
`MissingBeaconBlock` suggests that the database has corrupted. You should wipe the database and use [Checkpoint Sync](./checkpoint-sync.md) to resync the beacon chain.
### <a name="bn-download-slow"></a> After checkpoint sync, the progress of `downloading historical blocks` is slow. Why?
This is a normal behaviour. Since [v4.1.0](https://github.com/sigp/lighthouse/releases/tag/v4.1.0), Lighthouse implements rate-limited backfill sync to mitigate validator performance issues after a checkpoint sync. This is not something to worry about since backfill sync / historical data is not required for staking. However, if you opt to sync the chain as fast as possible, you can add the flag `--disable-backfill-rate-limiting` to the beacon node.
### <a name="bn-http"></a> My beacon node logs `WARN Error processing HTTP API request`, what should I do?
This warning usually comes with an http error code. Some examples are given below:
1. The log shows:
```
WARN Error processing HTTP API request method: GET, path: /eth/v1/validator/attestation_data, status: 500 Internal Server Error, elapsed: 305.65µs
```
The error is `500 Internal Server Error`. This suggests that the execution client is not synced. Once the execution client is synced, the error will disappear.
2. The log shows:
```
WARN Error processing HTTP API request method: POST, path: /eth/v1/validator/duties/attester/199565, status: 503 Service Unavailable, elapsed: 96.787µs
```
The error is `503 Service Unavailable`. This means that the beacon node is still syncing. When this happens, the validator client will log:
```
ERRO Failed to download attester duties err: FailedToDownloadAttesters("Some endpoints failed, num_failed: 2 http://localhost:5052/ => Unavailable(NotSynced), http://localhost:5052/ => RequestFailed(ServerMessage(ErrorMessage { code: 503, message: \"SERVICE_UNAVAILABLE: beacon node is syncing
```
This means that the validator client is sending requests to the beacon node. However, as the beacon node is still syncing, it is therefore unable to fulfil the request. The error will disappear once the beacon node is synced.
## Validator
### <a name="vc-activation"></a> Why does it take so long for a validator to be activated?
After validators create their execution layer deposit transaction there are two waiting After validators create their execution layer deposit transaction there are two waiting
periods before they can start producing blocks and attestations: periods before they can start producing blocks and attestations:
1. Waiting for the beacon chain to recognise the execution layer block containing the 1. Waiting for the beacon chain to recognise the execution layer block containing the
deposit (generally 4 to 7.4 hours). deposit (generally takes ~13.6 hours).
1. Waiting in the queue for validator activation (generally 6.4 minutes for 1. Waiting in the queue for validator activation.
every 4 validators in the queue).
Detailed answers below: Detailed answers below:
@ -32,33 +187,33 @@ Detailed answers below:
Since the beacon chain uses the execution layer for validator on-boarding, beacon chain Since the beacon chain uses the execution layer for validator on-boarding, beacon chain
validators must listen to event logs from the deposit contract. Since the validators must listen to event logs from the deposit contract. Since the
latest blocks of the execution chain are vulnerable to re-orgs due to minor network latest blocks of the execution chain are vulnerable to re-orgs due to minor network
partitions, beacon nodes follow the execution chain at a distance of 1,024 blocks partitions, beacon nodes follow the execution chain at a distance of 2048 blocks
(~4 hours) (see (~6.8 hours) (see
[`ETH1_FOLLOW_DISTANCE`](https://github.com/ethereum/consensus-specs/blob/v0.12.1/specs/phase0/validator.md#misc)). [`ETH1_FOLLOW_DISTANCE`](https://github.com/ethereum/consensus-specs/blob/v1.3.0/specs/phase0/validator.md#process-deposit)).
This follow distance protects the beacon chain from on-boarding validators that This follow distance protects the beacon chain from on-boarding validators that
are likely to be removed due to an execution chain re-org. are likely to be removed due to an execution chain re-org.
Now we know there's a 4 hours delay before the beacon nodes even _consider_ an Now we know there's a 6.8 hours delay before the beacon nodes even _consider_ an
execution layer block. Once they _are_ considering these blocks, there's a voting period execution layer block. Once they _are_ considering these blocks, there's a voting period
where beacon validators vote on which execution block hash to include in the beacon chain. This where beacon validators vote on which execution block hash to include in the beacon chain. This
period is defined as 32 epochs (~3.4 hours, see period is defined as 64 epochs (~6.8 hours, see
[`ETH1_VOTING_PERIOD`](https://github.com/ethereum/consensus-specs/blob/v0.12.1/specs/phase0/beacon-chain.md#time-parameters)). [`ETH1_VOTING_PERIOD`](https://github.com/ethereum/consensus-specs/blob/v1.3.0/specs/phase0/beacon-chain.md#time-parameters)).
During this voting period, each beacon block producer includes an During this voting period, each beacon block producer includes an
[`Eth1Data`](https://github.com/ethereum/consensus-specs/blob/v0.12.1/specs/phase0/beacon-chain.md#eth1data) [`Eth1Data`](https://github.com/ethereum/consensus-specs/blob/v1.3.0/specs/phase0/beacon-chain.md#eth1data)
in their block which counts as a vote towards what that validator considers to in their block which counts as a vote towards what that validator considers to
be the head of the execution chain at the start of the voting period (with respect be the head of the execution chain at the start of the voting period (with respect
to `ETH1_FOLLOW_DISTANCE`, of course). You can see the exact voting logic to `ETH1_FOLLOW_DISTANCE`, of course). You can see the exact voting logic
[here](https://github.com/ethereum/consensus-specs/blob/v0.12.1/specs/phase0/validator.md#eth1-data). [here](https://github.com/ethereum/consensus-specs/blob/v1.3.0/specs/phase0/validator.md#eth1-data).
These two delays combined represent the time between an execution layer deposit being These two delays combined represent the time between an execution layer deposit being
included in an execution data vote and that validator appearing in the beacon chain. included in an execution data vote and that validator appearing in the beacon chain.
The `ETH1_FOLLOW_DISTANCE` delay causes a minimum delay of ~4 hours and The `ETH1_FOLLOW_DISTANCE` delay causes a minimum delay of ~6.8 hours and
`ETH1_VOTING_PERIOD` means that if a validator deposit happens just _before_ `ETH1_VOTING_PERIOD` means that if a validator deposit happens just _before_
the start of a new voting period then they might not notice this delay at all. the start of a new voting period then they might not notice this delay at all.
However, if the validator deposit happens just _after_ the start of the new However, if the validator deposit happens just _after_ the start of the new
voting period the validator might have to wait ~3.4 hours for next voting voting period the validator might have to wait ~6.8 hours for next voting
period. In times of very, very severe network issues, the network may even fail period. In times of very severe network issues, the network may even fail
to vote in new execution layer blocks, stopping all new validator deposits! to vote in new execution layer blocks, thus stopping all new validator deposits and causing the wait to be longer.
#### 2. Waiting for a validator to be activated #### 2. Waiting for a validator to be activated
@ -68,30 +223,144 @@ They will simply be forgotten by the beacon chain! But, if those parameters were
correct, once the execution layer delays have elapsed and the validator appears in the correct, once the execution layer delays have elapsed and the validator appears in the
beacon chain, there's _another_ delay before the validator becomes "active" beacon chain, there's _another_ delay before the validator becomes "active"
(canonical definition (canonical definition
[here](https://github.com/ethereum/consensus-specs/blob/v0.12.1/specs/phase0/beacon-chain.md#is_active_validator)) and can start producing blocks and attestations. [here](https://github.com/ethereum/consensus-specs/blob/v1.3.0/specs/phase0/beacon-chain.md#is_active_validator)) and can start producing blocks and attestations.
Firstly, the validator won't become active until their beacon chain balance is Firstly, the validator won't become active until their beacon chain balance is
equal to or greater than equal to or greater than
[`MAX_EFFECTIVE_BALANCE`](https://github.com/ethereum/consensus-specs/blob/v0.12.1/specs/phase0/beacon-chain.md#gwei-values) [`MAX_EFFECTIVE_BALANCE`](https://github.com/ethereum/consensus-specs/blob/v1.3.0/specs/phase0/beacon-chain.md#gwei-values)
(32 ETH on mainnet, usually 3.2 ETH on testnets). Once this balance is reached, (32 ETH on mainnet, usually 3.2 ETH on testnets). Once this balance is reached,
the validator must wait until the start of the next epoch (up to 6.4 minutes) the validator must wait until the start of the next epoch (up to 6.4 minutes)
for the for the
[`process_registry_updates`](https://github.com/ethereum/consensus-specs/blob/v0.12.1/specs/phase0/beacon-chain.md#registry-updates) [`process_registry_updates`](https://github.com/ethereum/consensus-specs/blob/v1.3.0/specs/phase0/beacon-chain.md#registry-updates)
routine to run. This routine activates validators with respect to a [churn routine to run. This routine activates validators with respect to a [churn
limit](https://github.com/ethereum/consensus-specs/blob/v0.12.1/specs/phase0/beacon-chain.md#get_validator_churn_limit); limit](https://github.com/ethereum/consensus-specs/blob/v1.3.0/specs/phase0/beacon-chain.md#get_validator_churn_limit);
it will only allow the number of validators to increase (churn) by a certain it will only allow the number of validators to increase (churn) by a certain
amount. Up until there are about 330,000 validators this churn limit is set to amount. If a new validator isn't within the churn limit from the front of the queue,
4 and it starts to very slowly increase as the number of validators increases
from there.
If a new validator isn't within the churn limit from the front of the queue,
they will need to wait another epoch (6.4 minutes) for their next chance. This they will need to wait another epoch (6.4 minutes) for their next chance. This
repeats until the queue is cleared. repeats until the queue is cleared. The churn limit is summarised in the table below:
Once a validator has been activated, there's no more waiting! It's time to <div align="center" style="text-align: center;">
| Number of active validators | Validators activated per epoch | Validators activated per day |
|-------------------|--------------------------------------------|----|
| 327679 or less | 4 | 900 |
| 327680-393215 | 5 | 1125 |
| 393216-458751 | 6 | 1350
| 458752-524287 | 7 | 1575
| 524288-589823 | 8| 1800 |
| 589824-655359 | 9| 2025 |
| 655360-720895 | 10 | 2250|
| 720896-786431 | 11 | 2475 |
| 786432-851967 | 12 | 2700 |
| 851968-917503 | 13 | 2925 |
| 917504-983039 | 14 | 3150 |
| 983040-1048575 | 15 | 3375 |
</div>
For example, the number of active validators on Mainnet is about 574000 on May 2023. This means that 8 validators can be activated per epoch or 1800 per day (it is noted that the same applies to the exit queue). If, for example, there are 9000 validators waiting to be activated, this means that the waiting time can take up to 5 days.
Once a validator has been activated, congratulations! It's time to
produce blocks and attestations! produce blocks and attestations!
### Do I need to set up any port mappings? ### <a name="vc-redundancy"></a> Can I use redundancy in my staking setup?
You should **never** use duplicate/redundant validator keypairs or validator clients (i.e., don't
duplicate your JSON keystores and don't run `lighthouse vc` twice). This will lead to slashing.
However, there are some components which can be configured with redundancy. See the
[Redundancy](./redundancy.md) guide for more information.
### <a name="vc-missed-attestations"></a> I am missing attestations. Why?
The first thing is to ensure both consensus and execution clients are synced with the network. If they are synced, there may still be some issues with the node setup itself that is causing the missed attestations. Check the setup to ensure that:
- the clock is synced
- the computer has sufficient resources and is not overloaded
- the internet is working well
- you have sufficient peers
You can see more information on the [Ethstaker KB](https://ethstaker.gitbook.io/ethstaker-knowledge-base/help/missed-attestations). Once the above points are good, missing attestation should be a rare occurrence.
### <a name="vc-head-vote"></a> Sometimes I miss the attestation head vote, resulting in penalty. Is this normal?
In general, it is unavoidable to have some penalties occasionally. This is particularly the case when you are assigned to attest on the first slot of an epoch and if the proposer of that slot releases the block late, then you will get penalised for missing the target and head votes. Your attestation performance does not only depend on your own setup, but also on everyone elses performance.
### <a name="vc-exit"></a> Can I submit a voluntary exit message without running a beacon node?
Yes. Beaconcha.in provides the tool to broadcast the message. You can create the voluntary exit message file with [ethdo](https://github.com/wealdtech/ethdo/releases/tag/v1.30.0) and submit the message via the [beaconcha.in](https://beaconcha.in/tools/broadcast) website. A guide on how to use `ethdo` to perform voluntary exit can be found [here](https://github.com/eth-educators/ethstaker-guides/blob/main/voluntary-exit.md).
It is also noted that you can submit your BLS-to-execution-change message to update your withdrawal credentials from type `0x00` to `0x01` using the same link.
If you would like to still use Lighthouse to submit the message, you will need to run a beacon node and an execution client. For the beacon node, you can use checkpoint sync to quickly sync the chain under a minute. On the other hand, the execution client can be syncing and *needs not be synced*. This implies that it is possible to broadcast a voluntary exit message within a short time by quickly spinning up a node.
### <a name="vc-resource"></a> Does increasing the number of validators increase the CPU and other computer resources used?
A computer with hardware specifications stated in the [Recommended System Requirements](./installation.md#recommended-system-requirements) can run hundreds validators with only marginal increase in cpu usage. When validators are active, there is a bit of an increase in resources used from validators 0-64, because you end up subscribed to more subnets. After that, the increase in resources plateaus when the number of validators go from 64 to ~500.
### <a name="vc-reimport"></a> I want to add new validators. Do I have to reimport the existing keys?
No. You can just import new validator keys to the destination directory. If the `validator_keys` folder contains existing keys, that's fine as well because Lighthouse will skip importing existing keys.
### <a name="vc-import"></a> Do I have to stop `lighthouse vc` when importing new validator keys?
Generally yes.
If you do not want to stop `lighthouse vc`, you can use the [key manager API](./api-vc-endpoints.md) to import keys.
## Network, Monitoring and Maintenance
### <a name="net-peer"></a> I have a low peer count and it is not increasing
If you cannot find *ANY* peers at all, it is likely that you have incorrect
network configuration settings. Ensure that the network you wish to connect to
is correct (the beacon node outputs the network it is connecting to in the
initial boot-up log lines). On top of this, ensure that you are not using the
same `datadir` as a previous network, i.e., if you have been running the
`Goerli` testnet and are now trying to join a new network but using the same
`datadir` (the `datadir` is also printed out in the beacon node's logs on
boot-up).
If you find yourself with a low peer count and it's not reaching the target you
expect, there are a few things to check on:
1. Ensure that port forward was correctly set up as described [here](./advanced_networking.md#nat-traversal-port-forwarding).
To check that the ports are forwarded, run the command:
```bash
curl http://localhost:5052/lighthouse/nat
```
It should return `{"data":true}`. If it returns `{"data":false}`, you may want to double check if the port forward was correctly set up.
If the ports are open, you should have incoming peers. To check that you have incoming peers, run the command:
```bash
curl localhost:5052/lighthouse/peers | jq '.[] | select(.peer_info.connection_direction=="Incoming")'
```
If you have incoming peers, it should return a lot of data containing information of peers. If the response is empty, it means that you have no incoming peers and there the ports are not open. You may want to double check if the port forward was correctly set up.
2. Check that you do not lower the number of peers using the flag `--target-peers`. The default is 80. A lower value set will lower the maximum number of peers your node can connect to, which may potentially interrupt the validator performance. We recommend users to leave the `--target peers` untouched to keep a diverse set of peers.
3. Ensure that you have a quality router for the internet connection. For example, if you connect the router to many devices including the node, it may be possible that the router cannot handle all routing tasks, hence struggling to keep up the number of peers. Therefore, using a quality router for the node is important to keep a healthy number of peers.
### <a name="net-update"></a> How do I update lighthouse?
If you are updating to new release binaries, it will be the same process as described [here.](./installation-binaries.md)
If you are updating by rebuilding from source, see [here.](./installation-source.md#update-lighthouse)
If you are running the docker image provided by Sigma Prime on Dockerhub, you can update to specific versions, for example:
```bash
$ docker pull sigp/lighthouse:v1.0.0
```
If you are building a docker image, the process will be similar to the one described [here.](./docker.md#building-the-docker-image)
You just need to make sure the code you have checked out is up to date.
### <a name="net-port"></a> Do I need to set up any port mappings (port forwarding)?
It is not strictly required to open any ports for Lighthouse to connect and It is not strictly required to open any ports for Lighthouse to connect and
participate in the network. Lighthouse should work out-of-the-box. However, if participate in the network. Lighthouse should work out-of-the-box. However, if
@ -116,121 +385,105 @@ peers to join and degrades the overall connectivity of the global network.
For these reasons, we recommend that you make your node publicly accessible. For these reasons, we recommend that you make your node publicly accessible.
Lighthouse supports UPnP. If you are behind a NAT with a router that supports Lighthouse supports UPnP. If you are behind a NAT with a router that supports
UPnP you can simply ensure UPnP is enabled (Lighthouse will inform you in its UPnP, you can simply ensure UPnP is enabled (Lighthouse will inform you in its
initial logs if a route has been established). You can also manually set up initial logs if a route has been established). You can also manually [set up port mappings](./advanced_networking.md) in your router to your local Lighthouse instance. By default,
port mappings in your router to your local Lighthouse instance. By default,
Lighthouse uses port 9000 for both TCP and UDP. Opening both these ports will Lighthouse uses port 9000 for both TCP and UDP. Opening both these ports will
make your Lighthouse node maximally contactable. make your Lighthouse node maximally contactable.
### I have a low peer count and it is not increasing ### <a name="net-monitor"></a> How can I monitor my validators?
If you cannot find *ANY* peers at all. It is likely that you have incorrect
testnet configuration settings. Ensure that the network you wish to connect to
is correct (the beacon node outputs the network it is connecting to in the
initial boot-up log lines). On top of this, ensure that you are not using the
same `datadir` as a previous network. I.e if you have been running the
`prater` testnet and are now trying to join a new testnet but using the same
`datadir` (the `datadir` is also printed out in the beacon node's logs on
boot-up).
If you find yourself with a low peer count and it's not reaching the target you
expect. Try setting up the correct port forwards as described
[here](./advanced_networking.md#nat-traversal-port-forwarding).
### What should I do if I lose my slashing protection database?
See [here](./slashing-protection.md#misplaced-slashing-database).
### How do I update lighthouse?
If you are updating to new release binaries, it will be the same process as described [here.](./installation-binaries.md)
If you are updating by rebuilding from source, see [here.](./installation-source.md#update-lighthouse)
If you are running the docker image provided by Sigma Prime on Dockerhub, you can update to specific versions, for example:
```bash
$ docker pull sigp/lighthouse:v1.0.0
```
If you are building a docker image, the process will be similar to the one described [here.](./docker.md#building-the-docker-image)
You will just also need to make sure the code you have checked out is up to date.
### I can't compile lighthouse
See [here.](./installation-source.md#troubleshooting)
### What is "Syncing deposit contract block cache"?
```
Nov 30 21:04:28.268 WARN Syncing deposit contract block cache est_blocks_remaining: initializing deposits, service: slot_notifier
```
This log indicates that your beacon node is downloading blocks and deposits
from your execution node. When the `est_blocks_remaining` is
`initializing_deposits`, your node is downloading deposit logs. It may stay in
this stage for several minutes. Once the deposits logs are finished
downloading, the `est_blocks_remaining` value will start decreasing.
It is perfectly normal to see this log when starting a node for the first time
or after being off for more than several minutes.
If this log continues appearing sporadically during operation, there may be an
issue with your execution client endpoint.
### Can I use redundancy in my staking setup?
You should **never** use duplicate/redundant validator keypairs or validator clients (i.e., don't
duplicate your JSON keystores and don't run `lighthouse vc` twice). This will lead to slashing.
However, there are some components which can be configured with redundancy. See the
[Redundancy](./redundancy.md) guide for more information.
### How can I monitor my validators?
Apart from using block explorers, you may use the "Validator Monitor" built into Lighthouse which Apart from using block explorers, you may use the "Validator Monitor" built into Lighthouse which
provides logging and Prometheus/Grafana metrics for individual validators. See [Validator provides logging and Prometheus/Grafana metrics for individual validators. See [Validator
Monitoring](./validator-monitoring.md) for more information. Lighthouse has also developed Lighthouse UI (Siren) to monitor performance, see [Lighthouse UI (Siren)](./lighthouse-ui.md). Monitoring](./validator-monitoring.md) for more information. Lighthouse has also developed Lighthouse UI (Siren) to monitor performance, see [Lighthouse UI (Siren)](./lighthouse-ui.md).
### I see beacon logs showing `WARN: Execution engine called failed`, what should I do? ### <a name="net-bn-vc"></a> My beacon node and validator client are on different servers. How can I point the validator client to the beacon node?
The settings are as follows:
1. On the beacon node:
Specify `lighthouse bn --http-address local_IP` so that the beacon node is listening on the local network rather than on the `localhost`.
1. On the validator client:
Use the flag `--beacon-nodes` to point to the beacon node. For example, `lighthouse vc --beacon-nodes http://local_IP:5052` where `local_IP` is the local IP address of the beacon node and `5052` is the default `http-port` of the beacon node.
You can test that the setup is working with by running the following command on the validator client host:
```bash
curl "http://local_IP:5052/eth/v1/node/version"
```
You can refer to [Redundancy](./redundancy.md) for more information.
It is also worth noting that the `--beacon-nodes` flag can also be used for redundancy of beacon nodes. For example, let's say you have a beacon node and a validator client running on the same host, and a second beacon node on another server as a backup. In this case, you can use `lighthouse vc --beacon-nodes http://localhost:5052, http://local_IP:5052` on the validator client.
### <a name="net-ip"></a> Should I do anything to the beacon node or validator client settings if I have a relocation of the node / change of IP address?
No. Lighthouse will auto-detect the change and update your Ethereum Node Record (ENR). You just need to make sure you are not manually setting the ENR with `--enr-address` (which, for common use cases, this flag is not used).
## Miscellaneous
### <a name="misc-slashing"></a> What should I do if I lose my slashing protection database?
See [here](./slashing-protection.md#misplaced-slashing-database).
### <a name="misc-compile"></a> I can't compile lighthouse
See [here.](./installation-source.md#troubleshooting)
### <a name="misc-version"></a> How do I check the version of Lighthouse that is running?
If you build Lighthouse from source, run `lighthouse --version`. Example of output:
```bash
Lighthouse v4.1.0-693886b
BLS library: blst-modern
SHA256 hardware acceleration: false
Allocator: jemalloc
Specs: mainnet (true), minimal (false), gnosis (true)
```
If you download the binary file, navigate to the location of the directory, for example, the binary file is in `/usr/local/bin`, run `/usr/local/bin/lighthouse --version`, the example of output is the same as above.
Alternatively, if you have Lighthouse running, on the same computer, you can run:
```bash
curl "http://127.0.0.1:5052/eth/v1/node/version"
```
Example of output:
```bash
{"data":{"version":"Lighthouse/v4.1.0-693886b/x86_64-linux"}}
```
which says that the version is v4.1.0.
### <a name="misc-prune"></a> Does Lighthouse have pruning function like the execution client to save disk space?
There is no pruning of Lighthouse database for now. However, since v4.2.0, a feature to only sync back to the weak subjectivity point (approximately 5 months) when syncing via a checkpoint sync was added. This will help to save disk space since the previous behaviour will sync back to the genesis by default.
### <a name="misc-freezer"></a> Can I use a HDD for the freezer database and only have the hot db on SSD?
Yes, you can do so by using the flag `--freezer-dir /path/to/freezer_db` in the beacon node.
The `WARN Execution engine called failed` log is shown when the beacon node cannot reach the execution engine. When this warning occurs, it will be followed by a detailed message. A frequently encountered example of the error message is:
`error: Reqwest(reqwest::Error { kind: Request, url: Url { scheme: "http", cannot_be_a_base: false, username: "", password: None, host: Some(Ipv4(127.0.0.1)), port: Some(8551), path: "/", query: None, fragment: None }, source: TimedOut }), service: exec`
which says `TimedOut` at the end of the message. This means that the execution engine has not responded in time to the beacon node. There are a few reasons why this can occur:
1. The execution engine is not synced. Check the log of the execution engine to make sure that it is synced. If it is syncing, wait until it is synced and the error will disappear. You will see the beacon node logs `INFO Execution engine online` when it is synced.
1. The computer is overloaded. Check the CPU and RAM usage to see if it has overloaded. You can use `htop` to check for CPU and RAM usage.
1. Your SSD is slow. Check if your SSD is in "The Bad" list [here](https://gist.github.com/yorickdowne/f3a3e79a573bf35767cd002cc977b038). If your SSD is in "The Bad" list, it means it cannot keep in sync to the network and you may want to consider upgrading to a better SSD.
If the reason for the error message is caused by no. 1 above, you may want to look further. If the execution engine is out of sync suddenly, it is usually caused by ungraceful shutdown. The common causes for ungraceful shutdown are:
- Power outage. If power outages are an issue at your place, consider getting a UPS to avoid ungraceful shutdown of services.
- The service file is not stopped properly. To overcome this, make sure that the process is stop properly, e.g., during client updates.
- Out of memory (oom) error. This can happen when the system memory usage has reached its maximum and causes the execution engine to be killed. When this occurs, the log file will show `Main process exited, code=killed, status=9/KILL`. You can also run `sudo journalctl -a --since "18 hours ago" | grep -i "killed process` to confirm that the execution client has been killed due to oom. If you are using geth as the execution client, a short term solution is to reduce the resources used, for example: (1) reduce the cache by adding the flag `--cache 2048` (2) connect to less peers using the flag `--maxpeers 10`. If the oom occurs rather frequently, a long term solution is to increase the memory capacity of the computer.
### How do I check or update my withdrawal credentials?
Withdrawals will be available after the Capella/Shanghai upgrades on 12<sup>th</sup> April 2023. To check that if you are eligible for withdrawals, go to [Staking launchpad](https://launchpad.ethereum.org/en/withdrawals), enter your validator index and click `verify on mainnet`:
- `withdrawals enabled` means you will automatically receive withdrawals to the withdrawal address that you set.
- `withdrawals not enabled` means you will need to update your withdrawal credentials from `0x00` type to `0x01` type. The common way to do this is using `Staking deposit CLI` or `ethdo`, with the instructions available [here](https://launchpad.ethereum.org/en/withdrawals#update-your-keys).
For the case of `withdrawals not enabled`, you can update your withdrawal credentials **anytime**, and there is no deadline for that. The catch is that as long as you do not update your withdrawal credentials, your rewards in the beacon chain will continue to be locked in the beacon chain. Only after you update the withdrawal credentials, will the rewards be withdrawn to the withdrawal address.
### I am missing attestations. Why?
The first thing is to ensure both consensus and execution clients are synced with the network. If they are synced, there may still be some issues with the node setup itself that is causing the missed attestations. Check the setup to ensure that:
- the clock is synced
- the computer has sufficient resources and is not overloaded
- the internet is working well
- you have sufficient peers
You can see more information on the [Ethstaker KB](https://ethstaker.gitbook.io/ethstaker-knowledge-base/help/missed-attestations). Once the above points are good, missing attestation should be a rare occurance.
### Sometimes I miss the attestation head vote, resulting in penalty. Is this normal?
In general it is unavoiadable to have some penalties occasionally. This is particularly the case when you are assigned to attest on the first slot of an epoch and if the proposer of that slot releases the block late, then you will get penalised for missing the target and head votes. Your attestation performance does not only depend on your own setup, but also on everyone else's performance.
### My beacon node is stuck at downloading historical block using checkpoing sync. What can I do?
Check the number of peers you are connected to. If you have low peers (less than 50), try to do port forwarding on the port 9000 TCP/UDP to increase peer count.

View File

@ -3,7 +3,7 @@
[launchpad]: https://launchpad.ethereum.org/ [launchpad]: https://launchpad.ethereum.org/
> >
> **Note: While Lighthouse is able to generate the validator keys and the deposit data file to submit to the deposit contract, we strongly recommend using the [staking-deposit-cli](https://github.com/ethereum/staking-deposit-cli) to create validators keys and the deposit data file. This is because the [staking-deposit-cli](https://github.com/ethereum/staking-deposit-cli) which has the option to assign a withdrawal address during the key generation process, while Lighthouse wallet will always generate keys with withdrawal credentials of type 0x00. This means that users who created keys using Lighthouse will have to update their withdrawal credentials in the future to enable withdrawals. In addition, Lighthouse generates the deposit data file in the form of `*.rlp`, which cannot be uploaded to the [Staking launchpad][launchpad] that accepts only `*.json` file. This means that users have to directly interact with the deposit contract to be able to submit the deposit if they were to generate the files using Lighthouse.** > **Note: While Lighthouse is able to generate the validator keys and the deposit data file to submit to the deposit contract, we strongly recommend using the [staking-deposit-cli](https://github.com/ethereum/staking-deposit-cli) to create validators keys and the deposit data file. This is because the [staking-deposit-cli](https://github.com/ethereum/staking-deposit-cli) has the option to assign a withdrawal address during the key generation process, while Lighthouse wallet will always generate keys with withdrawal credentials of type 0x00. This means that users who created keys using Lighthouse will have to update their withdrawal credentials in the future to enable withdrawals. In addition, Lighthouse generates the deposit data file in the form of `*.rlp`, which cannot be uploaded to the [Staking launchpad][launchpad] that accepts only `*.json` file. This means that users have to directly interact with the deposit contract to be able to submit the deposit if they were to generate the files using Lighthouse.**
Lighthouse uses a _hierarchical_ key management system for producing validator Lighthouse uses a _hierarchical_ key management system for producing validator
keys. It is hierarchical because each validator key can be _derived_ from a keys. It is hierarchical because each validator key can be _derived_ from a

View File

@ -1,15 +1,13 @@
# Merge Migration # Merge Migration
This document provides detail for users who want to run a Lighthouse node on post-merge Ethereum. [The Merge](https://ethereum.org/en/roadmap/merge/) has occurred on mainnet on 15<sup>th</sup> September 2022. This document provides detail of what users need to do in the past (before The Merge) to run a Lighthouse node on a post-merge Ethereum network. This document now serves as a record of the milestone upgrade.
> The merge occurred on mainnet in September 2022.
## Necessary Configuration ## Necessary Configuration
There are two configuration changes required for a Lighthouse node to operate correctly throughout There are two configuration changes required for a Lighthouse node to operate correctly throughout
the merge: the merge:
1. You *must* run your own execution engine such as Geth or Nethermind alongside Lighthouse. 1. You *must* run your own execution engine such as Besu, Erigon, Geth or Nethermind alongside Lighthouse.
You *must* update your `lighthouse bn` configuration to connect to the execution engine using new You *must* update your `lighthouse bn` configuration to connect to the execution engine using new
flags which are documented on this page in the flags which are documented on this page in the
[Connecting to an execution engine](#connecting-to-an-execution-engine) section. [Connecting to an execution engine](#connecting-to-an-execution-engine) section.
@ -23,12 +21,19 @@ engine to a merge-ready version.
## When? ## When?
You must configure your node to be merge-ready before the Bellatrix fork occurs on the network All networks (**Mainnet**, **Goerli (Prater)**, **Ropsten**, **Sepolia**, **Kiln**, **Gnosis**) have successfully undergone the Bellatrix fork and transitioned to a post-merge Network. Your node must have a merge-ready configuration to continue operating. Table below lists the date at which Bellatrix and The Merge occurred:
on which your node is operating.
* **Gnosis**: the Bellatrix fork has not yet been scheduled. <div align="center">
* **Mainnet**, **Goerli (Prater)**, **Ropsten**, **Sepolia**, **Kiln**: the Bellatrix fork has
already occurred. You must have a merge-ready configuration right now. | Network | Bellatrix | The Merge | Remark |
|-------------------|--------------------------------------------|----|----|
| Ropsten | 2<sup>nd</sup> June 2022 | 8<sup>th</sup> June 2022 | Deprecated
| Sepolia | 20<sup>th</sup> June 2022 | 6<sup>th</sup> July 2022 | |
| Goerli | 4<sup>th</sup> August 2022 | 10<sup>th</sup> August 2022 | Previously named `Prater`|
| Mainnet | 6<sup>th</sup> September 2022 | 15<sup>th</sup> September 2022 |
| Gnosis| 30<sup>th</sup> November 2022 | 8<sup>th</sup> December 2022
</div>
## Connecting to an execution engine ## Connecting to an execution engine
@ -42,7 +47,7 @@ present in post-merge blocks. Two new flags are used to configure this connectio
If you set up an execution engine with `--execution-endpoint` then you *must* provide a JWT secret If you set up an execution engine with `--execution-endpoint` then you *must* provide a JWT secret
using `--execution-jwt`. This is a mandatory form of authentication that ensures that Lighthouse using `--execution-jwt`. This is a mandatory form of authentication that ensures that Lighthouse
has authority to control the execution engine. has the authority to control the execution engine.
> Tip: the --execution-jwt-secret-key <STRING> flag can be used instead of --execution-jwt <FILE>. > Tip: the --execution-jwt-secret-key <STRING> flag can be used instead of --execution-jwt <FILE>.
> This is useful, for example, for users who wish to inject the value into a Docker container without > This is useful, for example, for users who wish to inject the value into a Docker container without
@ -89,7 +94,7 @@ lighthouse \
beacon_node \ beacon_node \
--http \ --http \
--execution-endpoint http://localhost:8551 --execution-endpoint http://localhost:8551
--execution-jwt ~/.ethereum/geth/jwtsecret --execution-jwt /path/to/jwtsecret
``` ```
The changes here are: The changes here are:
@ -105,8 +110,7 @@ The changes here are:
not be `8551`, see their documentation for details. not be `8551`, see their documentation for details.
3. Add the `--execution-jwt` flag. 3. Add the `--execution-jwt` flag.
- This is the path to a file containing a 32-byte secret for authenticating the BN with the - This is the path to a file containing a 32-byte secret for authenticating the BN with the
execution engine. In this example our execution engine is Geth, so we've chosen the default execution engine. It is critical that both
location for Geth. Your execution engine might have a different path. It is critical that both
the BN and execution engine reference a file with the same value, otherwise they'll fail to the BN and execution engine reference a file with the same value, otherwise they'll fail to
communicate. communicate.
@ -118,7 +122,7 @@ a deprecation warning will be logged and Lighthouse *may* remove these flags in
### The relationship between `--eth1-endpoints` and `--execution-endpoint` ### The relationship between `--eth1-endpoints` and `--execution-endpoint`
Pre-merge users will be familiar with the `--eth1-endpoints` flag. This provides a list of Ethereum Pre-merge users will be familiar with the `--eth1-endpoints` flag. This provides a list of Ethereum
"eth1" nodes (e.g., Geth, Nethermind, etc). Each beacon node (BN) can have multiple eth1 endpoints "eth1" nodes (Besu, Erigon, Geth or Nethermind). Each beacon node (BN) can have multiple eth1 endpoints
and each eth1 endpoint can have many BNs connection (many-to-many relationship). The eth1 node and each eth1 endpoint can have many BNs connection (many-to-many relationship). The eth1 node
provides a source of truth for the [deposit provides a source of truth for the [deposit
contract](https://ethereum.org/en/staking/deposit-contract/) and beacon chain proposers include this contract](https://ethereum.org/en/staking/deposit-contract/) and beacon chain proposers include this
@ -129,7 +133,7 @@ achieve this.
To progress through the Bellatrix upgrade nodes will need a *new* connection to an "eth1" node; To progress through the Bellatrix upgrade nodes will need a *new* connection to an "eth1" node;
`--execution-endpoint`. This connection has a few different properties. Firstly, the term "eth1 `--execution-endpoint`. This connection has a few different properties. Firstly, the term "eth1
node" has been deprecated and replaced with "execution engine". Whilst "eth1 node" and "execution node" has been deprecated and replaced with "execution engine". Whilst "eth1 node" and "execution
engine" still refer to the same projects (Geth, Nethermind, etc) the former refers to the pre-merge engine" still refer to the same projects (Besu, Erigon, Geth or Nethermind), the former refers to the pre-merge
versions and the latter refers to post-merge versions. Secondly, there is a strict one-to-one versions and the latter refers to post-merge versions. Secondly, there is a strict one-to-one
relationship between Lighthouse and the execution engine; only one Lighthouse node can connect to relationship between Lighthouse and the execution engine; only one Lighthouse node can connect to
one execution engine. Thirdly, it is impossible to fully verify the post-merge chain without an one execution engine. Thirdly, it is impossible to fully verify the post-merge chain without an
@ -139,7 +143,7 @@ impossible to reliably *propose* blocks without it.
Since an execution engine is a hard requirement in the post-merge chain and the execution engine Since an execution engine is a hard requirement in the post-merge chain and the execution engine
contains the transaction history of the Ethereum chain, there is no longer a need for the contains the transaction history of the Ethereum chain, there is no longer a need for the
`--eth1-endpoints` flag for information about the deposit contract. The `--execution-endpoint` can `--eth1-endpoints` flag for information about the deposit contract. The `--execution-endpoint` can
be used for all such queries. Therefore we can say that where `--execution-endpoint` is included be used for all such queries. Therefore we can say that where `--execution-endpoint` is included,
`--eth1-endpoints` should be omitted. `--eth1-endpoints` should be omitted.
## FAQ ## FAQ

View File

@ -8,7 +8,7 @@ There are three places in Lighthouse where redundancy is notable:
1. ❌ NOT SUPPORTED: Using a redundant execution node in `lighthouse bn --execution-endpoint` 1. ❌ NOT SUPPORTED: Using a redundant execution node in `lighthouse bn --execution-endpoint`
1. ☠️ BAD: Running redundant `lighthouse vc` instances with overlapping keypairs. 1. ☠️ BAD: Running redundant `lighthouse vc` instances with overlapping keypairs.
I mention (3) since it is unsafe and should not be confused with the other two We mention (3) since it is unsafe and should not be confused with the other two
uses of redundancy. **Running the same validator keypair in more than one uses of redundancy. **Running the same validator keypair in more than one
validator client (Lighthouse, or otherwise) will eventually lead to slashing.** validator client (Lighthouse, or otherwise) will eventually lead to slashing.**
See [Slashing Protection](./slashing-protection.md) for more information. See [Slashing Protection](./slashing-protection.md) for more information.
@ -49,6 +49,7 @@ There are a few interesting properties about the list of `--beacon-nodes`:
> provided (if it is desired). It will only be used as default if no `--beacon-nodes` flag is > provided (if it is desired). It will only be used as default if no `--beacon-nodes` flag is
> provided at all. > provided at all.
### Configuring a redundant Beacon Node ### Configuring a redundant Beacon Node
In our previous example, we listed `http://192.168.1.1:5052` as a redundant In our previous example, we listed `http://192.168.1.1:5052` as a redundant
@ -56,10 +57,9 @@ node. Apart from having sufficient resources, the backup node should have the
following flags: following flags:
- `--http`: starts the HTTP API server. - `--http`: starts the HTTP API server.
- `--http-address 0.0.0.0`: this allows *any* external IP address to access the - `--http-address local_IP`: where `local_IP` is the private IP address of the computer running the beacon node. This is only required if your backup beacon node is on a different host.
HTTP server (a firewall should be configured to deny unauthorized access to port > Note: You could also use `--http-address 0.0.0.0`, but this allows *any* external IP address to access the HTTP server. As such, a firewall should be configured to deny unauthorized access to port `5052`.
`5052`). This is only required if your backup node is on a different host. - `--execution-endpoint`: see [Merge Migration](./merge-migration.md).
- `--execution-endpoint`: see [Merge Migration](./merge-migration.md).
- `--execution-jwt`: see [Merge Migration](./merge-migration.md). - `--execution-jwt`: see [Merge Migration](./merge-migration.md).
For example one could use the following command to provide a backup beacon node: For example one could use the following command to provide a backup beacon node:
@ -67,7 +67,7 @@ For example one could use the following command to provide a backup beacon node:
```bash ```bash
lighthouse bn \ lighthouse bn \
--http \ --http \
--http-address 0.0.0.0 \ --http-address local_IP \
--execution-endpoint http://localhost:8551 \ --execution-endpoint http://localhost:8551 \
--execution-jwt /secrets/jwt.hex --execution-jwt /secrets/jwt.hex
``` ```

View File

@ -18,7 +18,9 @@ The additional requirements for developers are:
the networking stack. the networking stack.
- [`java 11 runtime`](https://openjdk.java.net/projects/jdk/). 11 is the minimum, - [`java 11 runtime`](https://openjdk.java.net/projects/jdk/). 11 is the minimum,
used by web3signer_tests. used by web3signer_tests.
- [`libpq-dev`](https://www.postgresql.org/docs/devel/libpq.html). Also know as
`libpq-devel` on some systems.
- [`docker`](https://www.docker.com/). Some tests need docker installed and **running**.
## Using `make` ## Using `make`
Commands to run the test suite are available via the `Makefile` in the Commands to run the test suite are available via the `Makefile` in the

View File

@ -8,10 +8,9 @@ extra income for your validators. However it is currently only recommended for e
of the immaturity of the slasher UX and the extra resources required. of the immaturity of the slasher UX and the extra resources required.
## Minimum System Requirements ## Minimum System Requirements
* Quad-core CPU * Quad-core CPU
* 16 GB RAM * 16 GB RAM
* 256 GB solid state storage (in addition to space for the beacon node DB) * 256 GB solid state storage (in addition to the space requirement for the beacon node DB)
## How to Run ## How to Run
@ -28,7 +27,7 @@ messages are filtered for relevancy, and all relevant messages are checked for s
to the slasher database. to the slasher database.
You **should** run with debug logs, so that you can see the slasher's internal machinations, and You **should** run with debug logs, so that you can see the slasher's internal machinations, and
provide logs to the devs should you encounter any bugs. provide logs to the developers should you encounter any bugs.
## Configuration ## Configuration
@ -97,7 +96,7 @@ Both database backends LMDB and MDBX place a hard limit on the size of the datab
file. You can use the `--slasher-max-db-size` flag to set this limit. It can be adjusted after file. You can use the `--slasher-max-db-size` flag to set this limit. It can be adjusted after
initialization if the limit is reached. initialization if the limit is reached.
By default the limit is set to accommodate the default history length and around 300K validators but By default the limit is set to accommodate the default history length and around 600K validators (with about 30% headroom) but
you can set it lower if running with a reduced history length. The space required scales you can set it lower if running with a reduced history length. The space required scales
approximately linearly in validator count and history length, i.e. if you halve either you can halve approximately linearly in validator count and history length, i.e. if you halve either you can halve
the space required. the space required.
@ -108,7 +107,7 @@ If you want an estimate of the database size you can use this formula:
4.56 GB * (N / 256) * (V / 250000) 4.56 GB * (N / 256) * (V / 250000)
``` ```
where `V` is the validator count and `N` is the history length. where `N` is the history length and `V` is the validator count.
You should set the maximum size higher than the estimate to allow room for growth in the validator You should set the maximum size higher than the estimate to allow room for growth in the validator
count. count.

View File

@ -103,6 +103,8 @@ client.
} }
``` ```
Command:
```bash ```bash
DATADIR=$HOME/.lighthouse/mainnet DATADIR=$HOME/.lighthouse/mainnet
PUBKEY=0xa9735061c84fc0003657e5bd38160762b7ef2d67d280e00347b1781570088c32c06f15418c144949f5d736b1d3a6c591 PUBKEY=0xa9735061c84fc0003657e5bd38160762b7ef2d67d280e00347b1781570088c32c06f15418c144949f5d736b1d3a6c591
@ -115,11 +117,15 @@ curl -X POST \
http://localhost:5062/eth/v1/validator/${PUBKEY}/feerecipient | jq http://localhost:5062/eth/v1/validator/${PUBKEY}/feerecipient | jq
``` ```
Note that an authorization header is required to interact with the API. This is specified with the header `-H "Authorization: Bearer $(cat ${DATADIR}/validators/api-token.txt)"` which read the API token to supply the authentication. Refer to [Authorization Header](./api-vc-auth-header.md) for more information. If you are having permission issue with accessing the API token file, you can modify the header to become `-H "Authorization: Bearer $(sudo cat ${DATADIR}/validators/api-token.txt)"`.
#### Successful Response (202) #### Successful Response (202)
```json ```json
null null
``` ```
A `null` response indicates that the request is successful.
### Querying the fee recipient ### Querying the fee recipient
The same path with a `GET` request can be used to query the fee recipient for a given public key at any time. The same path with a `GET` request can be used to query the fee recipient for a given public key at any time.
@ -131,6 +137,8 @@ The same path with a `GET` request can be used to query the fee recipient for a
| Required Headers | [`Authorization`](./api-vc-auth-header.md) | | Required Headers | [`Authorization`](./api-vc-auth-header.md) |
| Typical Responses | 200, 404 | | Typical Responses | 200, 404 |
Command:
```bash ```bash
DATADIR=$HOME/.lighthouse/mainnet DATADIR=$HOME/.lighthouse/mainnet
PUBKEY=0xa9735061c84fc0003657e5bd38160762b7ef2d67d280e00347b1781570088c32c06f15418c144949f5d736b1d3a6c591 PUBKEY=0xa9735061c84fc0003657e5bd38160762b7ef2d67d280e00347b1781570088c32c06f15418c144949f5d736b1d3a6c591
@ -163,6 +171,8 @@ This is useful if you want the fee recipient to fall back to the validator clien
| Required Headers | [`Authorization`](./api-vc-auth-header.md) | | Required Headers | [`Authorization`](./api-vc-auth-header.md) |
| Typical Responses | 204, 404 | | Typical Responses | 204, 404 |
Command:
```bash ```bash
DATADIR=$HOME/.lighthouse/mainnet DATADIR=$HOME/.lighthouse/mainnet
PUBKEY=0xa9735061c84fc0003657e5bd38160762b7ef2d67d280e00347b1781570088c32c06f15418c144949f5d736b1d3a6c591 PUBKEY=0xa9735061c84fc0003657e5bd38160762b7ef2d67d280e00347b1781570088c32c06f15418c144949f5d736b1d3a6c591

View File

@ -12,9 +12,20 @@ following configuration screen.
This allows you to enter the address and ports of the associated Lighthouse This allows you to enter the address and ports of the associated Lighthouse
Beacon node and Lighthouse Validator client. Beacon node and Lighthouse Validator client.
> The Beacon Node must be run with the `--gui` flag set. To allow the browser > The Beacon Node must be run with the `--gui` flag set.
> to access the node beyond your local computer you also need to allow CORS in
> the http API. This can be done via `--http-allow-origin "*"`. If you run Siren in the browser (by entering `localhost` in the browser), you will need to allow CORS in the HTTP API. This can be done by adding the flag `--http-allow-origin "*"` for both beacon node and validator client. If you would like to access Siren beyond the local computer, we recommend using an SSH tunnel. This requires a tunnel for 3 ports: `80` (assuming the port is unchanged as per the [installation guide](./ui-installation.md#docker-recommended), `5052` (for beacon node) and `5062` (for validator client). You can use the command below to perform SSH tunneling:
```bash
ssh -N -L 80:127.0.0.1:80 -L 5052:127.0.0.1:5052 -L 5062:127.0.0.1:5062 username@local_ip
```
where `username` is the username of the server and `local_ip` is the local IP address of the server. Note that with the `-N` option in an SSH session, you will not be able to execute commands in the CLI to avoid confusion with ordinary shell sessions. The connection will appear to be "hung" upon a successful connection, but that is normal. Once you have successfully connected to the server via SSH tunneling, you should be able to access Siren by entering `localhost` in a web browser.
You can also access Siren using the app downloaded in the [Siren release page](https://github.com/sigp/siren/releases). To access Siren beyond the local computer, you can use SSH tunneling for ports `5052` and `5062` using the command:
```bash
ssh -N -L 5052:127.0.0.1:5052 -L 5062:127.0.0.1:5062 username@local_ip
```
A green tick will appear once Siren is able to connect to both clients. You A green tick will appear once Siren is able to connect to both clients. You
can specify different ports for each client by clicking on the advanced tab. can specify different ports for each client by clicking on the advanced tab.
@ -33,7 +44,7 @@ The token is located in the default data directory of the validator
client. The default path is client. The default path is
`~/.lighthouse/<network>/validators/api-token.txt`. `~/.lighthouse/<network>/validators/api-token.txt`.
The contents of this file for the desired valdiator client needs to be The contents of this file for the desired validator client needs to be
entered. entered.
## Name ## Name

View File

@ -4,13 +4,13 @@
Yes, Siren requires Lighthouse v3.5.1 or higher to function properly. These releases can be found on the [releases](https://github.com/sigp/lighthouse/releases) page of the Lighthouse repository. Yes, Siren requires Lighthouse v3.5.1 or higher to function properly. These releases can be found on the [releases](https://github.com/sigp/lighthouse/releases) page of the Lighthouse repository.
## 2. Where can I find my API token? ## 2. Where can I find my API token?
The required Api token may be found in the default data directory of the validator client. For more information please refer to the lighthouse ui configuration [`api token section`](./ui-configuration.md#api-token). The required Api token may be found in the default data directory of the validator client. For more information please refer to the lighthouse ui configuration [`api token section`](./api-vc-auth-header.md).
## 3. How do I fix the Node Network Errors? ## 3. How do I fix the Node Network Errors?
If you recieve a red notification with a BEACON or VALIDATOR NODE NETWORK ERROR you can refer to the lighthouse ui configuration and [`connecting to clients section`](./ui-configuration.md#connecting-to-the-clients). If you receive a red notification with a BEACON or VALIDATOR NODE NETWORK ERROR you can refer to the lighthouse ui configuration and [`connecting to clients section`](./ui-configuration.md#connecting-to-the-clients).
## 4. How do I change my Beacon or Validator address after logging in? ## 4. How do I change my Beacon or Validator address after logging in?
Once you have successfully arrived to the main dashboard, use the sidebar to access the settings view. In the top right hand corner there is a `Configurtion` action button that will redirect you back to the configuration screen where you can make appropriate changes. Once you have successfully arrived to the main dashboard, use the sidebar to access the settings view. In the top right hand corner there is a `Configuration` action button that will redirect you back to the configuration screen where you can make appropriate changes.
## 5. Why doesn't my validator balance graph show any data? ## 5. Why doesn't my validator balance graph show any data?
If your graph is not showing data, it usually means your validator node is still caching data. The application must wait at least 3 epochs before it can render any graphical visualizations. This could take up to 20min. If your graph is not showing data, it usually means your validator node is still caching data. The application must wait at least 3 epochs before it can render any graphical visualizations. This could take up to 20min.

View File

@ -10,7 +10,7 @@ Siren's dashboard view provides a summary of all performance and key validator m
The account earnings component accumulates reward data from all registered validators providing a summation of total rewards earned while staking. Given current conversion rates, this component also converts your balance into your selected fiat currency. The account earnings component accumulates reward data from all registered validators providing a summation of total rewards earned while staking. Given current conversion rates, this component also converts your balance into your selected fiat currency.
Below in the earning section, you can also view your total earnings or click the adjacent buttons to view your estimated earnings given a specific timeframe based on current device and network conditions. Below in the earning section, you can also view your total earnings or click the adjacent buttons to view your estimated earnings given a specific time frame based on current device and network conditions.
![](imgs/ui-account-earnings.png) ![](imgs/ui-account-earnings.png)

View File

@ -8,6 +8,8 @@ These endpoints are not stable or included in the Ethereum consensus standard AP
they are subject to change or removal without a change in major release they are subject to change or removal without a change in major release
version. version.
In order to apply these APIs, you need to have historical states information in the database of your node. This means adding the flag `--reconstruct-historic-states` in the beacon node or using the [/lighthouse/database/reconstruct API](./api-lighthouse.md#lighthousedatabasereconstruct). Once the state reconstruction process is completed, you can apply these APIs to any epoch.
## Endpoints ## Endpoints
HTTP Path | Description | HTTP Path | Description |
@ -29,7 +31,7 @@ is not the case for attestations from the _previous_ epoch.
``` ```
`epoch` query parameter `epoch` query parameter
| |
| --------- values are calcuated here | --------- values are calculated here
| | | |
v v v v
Epoch: |---previous---|---current---|---next---| Epoch: |---previous---|---current---|---next---|

View File

@ -5,7 +5,7 @@
[Teku]: https://github.com/consensys/teku [Teku]: https://github.com/consensys/teku
[Web3Signer] is a tool by Consensys which allows *remote signing*. Remote signing is when a [Web3Signer] is a tool by Consensys which allows *remote signing*. Remote signing is when a
Validator Client (VC) out-sources the signing of messages to remote server (e.g., via HTTPS). This Validator Client (VC) out-sources the signing of messages to a remote server (e.g., via HTTPS). This
means that the VC does not hold the validator private keys. means that the VC does not hold the validator private keys.
## Warnings ## Warnings
@ -47,7 +47,7 @@ remote signer:
client_identity_password: "password" client_identity_password: "password"
``` ```
When using this file, the Lighthouse VC will perform duties for the `0xa5566..` validator and defer When using this file, the Lighthouse VC will perform duties for the `0xa5566..` validator and refer
to the `https://my-remote-signer.com:1234` server to obtain any signatures. It will load a to the `https://my-remote-signer.com:1234` server to obtain any signatures. It will load a
"self-signed" SSL certificate from `/home/paul/my-certificates/my-remote-signer.pem` (on the "self-signed" SSL certificate from `/home/paul/my-certificates/my-remote-signer.pem` (on the
filesystem of the VC) to encrypt the communications between the VC and Web3Signer. It will use filesystem of the VC) to encrypt the communications between the VC and Web3Signer. It will use

View File

@ -89,3 +89,7 @@ PROPOSER_SCORE_BOOST: 40
DEPOSIT_CHAIN_ID: 100 DEPOSIT_CHAIN_ID: 100
DEPOSIT_NETWORK_ID: 100 DEPOSIT_NETWORK_ID: 100
DEPOSIT_CONTRACT_ADDRESS: 0x0B98057eA310F4d31F2a452B414647007d1645d9 DEPOSIT_CONTRACT_ADDRESS: 0x0B98057eA310F4d31F2a452B414647007d1645d9
# Network
# ---------------------------------------------------------------
SUBNETS_PER_NODE: 4

View File

@ -89,3 +89,7 @@ PROPOSER_SCORE_BOOST: 40
DEPOSIT_CHAIN_ID: 1 DEPOSIT_CHAIN_ID: 1
DEPOSIT_NETWORK_ID: 1 DEPOSIT_NETWORK_ID: 1
DEPOSIT_CONTRACT_ADDRESS: 0x00000000219ab540356cBB839Cbe05303d7705Fa DEPOSIT_CONTRACT_ADDRESS: 0x00000000219ab540356cBB839Cbe05303d7705Fa
# Network
# ---------------------------------------------------------------
SUBNETS_PER_NODE: 2

View File

@ -86,3 +86,7 @@ DEPOSIT_CHAIN_ID: 5
DEPOSIT_NETWORK_ID: 5 DEPOSIT_NETWORK_ID: 5
# Prater test deposit contract on Goerli Testnet # Prater test deposit contract on Goerli Testnet
DEPOSIT_CONTRACT_ADDRESS: 0xff50ed3d0ec03aC01D4C79aAd74928BFF48a7b2b DEPOSIT_CONTRACT_ADDRESS: 0xff50ed3d0ec03aC01D4C79aAd74928BFF48a7b2b
# Network
# ---------------------------------------------------------------
SUBNETS_PER_NODE: 2

View File

@ -78,3 +78,7 @@ PROPOSER_SCORE_BOOST: 40
DEPOSIT_CHAIN_ID: 11155111 DEPOSIT_CHAIN_ID: 11155111
DEPOSIT_NETWORK_ID: 11155111 DEPOSIT_NETWORK_ID: 11155111
DEPOSIT_CONTRACT_ADDRESS: 0x7f02C3E3c98b133055B8B348B2Ac625669Ed295D DEPOSIT_CONTRACT_ADDRESS: 0x7f02C3E3c98b133055B8B348B2Ac625669Ed295D
# Network
# ---------------------------------------------------------------
SUBNETS_PER_NODE: 2

View File

@ -1,4 +1,4 @@
///! This implements a time-based LRU cache for fast checking of duplicates //! This implements a time-based LRU cache for fast checking of duplicates
use fnv::FnvHashSet; use fnv::FnvHashSet;
use std::collections::VecDeque; use std::collections::VecDeque;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};

View File

@ -176,11 +176,9 @@ pub struct ChainSpec {
pub maximum_gossip_clock_disparity_millis: u64, pub maximum_gossip_clock_disparity_millis: u64,
pub target_aggregators_per_committee: u64, pub target_aggregators_per_committee: u64,
pub attestation_subnet_count: u64, pub attestation_subnet_count: u64,
pub random_subnets_per_validator: u64,
pub epochs_per_random_subnet_subscription: u64,
pub subnets_per_node: u8, pub subnets_per_node: u8,
pub epochs_per_subnet_subscription: u64, pub epochs_per_subnet_subscription: u64,
attestation_subnet_extra_bits: u8, pub attestation_subnet_extra_bits: u8,
/* /*
* Application params * Application params
@ -472,17 +470,7 @@ impl ChainSpec {
#[allow(clippy::integer_arithmetic)] #[allow(clippy::integer_arithmetic)]
pub const fn attestation_subnet_prefix_bits(&self) -> u32 { pub const fn attestation_subnet_prefix_bits(&self) -> u32 {
// maybe use log2 when stable https://github.com/rust-lang/rust/issues/70887 let attestation_subnet_count_bits = self.attestation_subnet_count.ilog2();
// NOTE: this line is here simply to guarantee that if self.attestation_subnet_count type
// is changed, a compiler warning will be raised. This code depends on the type being u64.
let attestation_subnet_count: u64 = self.attestation_subnet_count;
let attestation_subnet_count_bits = if attestation_subnet_count == 0 {
0
} else {
63 - attestation_subnet_count.leading_zeros()
};
self.attestation_subnet_extra_bits as u32 + attestation_subnet_count_bits self.attestation_subnet_extra_bits as u32 + attestation_subnet_count_bits
} }
@ -649,13 +637,11 @@ impl ChainSpec {
network_id: 1, // mainnet network id network_id: 1, // mainnet network id
attestation_propagation_slot_range: 32, attestation_propagation_slot_range: 32,
attestation_subnet_count: 64, attestation_subnet_count: 64,
random_subnets_per_validator: 1, subnets_per_node: 2,
subnets_per_node: 1,
maximum_gossip_clock_disparity_millis: 500, maximum_gossip_clock_disparity_millis: 500,
target_aggregators_per_committee: 16, target_aggregators_per_committee: 16,
epochs_per_random_subnet_subscription: 256,
epochs_per_subnet_subscription: 256, epochs_per_subnet_subscription: 256,
attestation_subnet_extra_bits: 6, attestation_subnet_extra_bits: 0,
/* /*
* Application specific * Application specific
@ -886,13 +872,11 @@ impl ChainSpec {
network_id: 100, // Gnosis Chain network id network_id: 100, // Gnosis Chain network id
attestation_propagation_slot_range: 32, attestation_propagation_slot_range: 32,
attestation_subnet_count: 64, attestation_subnet_count: 64,
random_subnets_per_validator: 1, subnets_per_node: 4, // Make this larger than usual to avoid network damage
subnets_per_node: 1,
maximum_gossip_clock_disparity_millis: 500, maximum_gossip_clock_disparity_millis: 500,
target_aggregators_per_committee: 16, target_aggregators_per_committee: 16,
epochs_per_random_subnet_subscription: 256,
epochs_per_subnet_subscription: 256, epochs_per_subnet_subscription: 256,
attestation_subnet_extra_bits: 6, attestation_subnet_extra_bits: 0,
/* /*
* Application specific * Application specific
@ -988,6 +972,9 @@ pub struct Config {
shard_committee_period: u64, shard_committee_period: u64,
#[serde(with = "serde_utils::quoted_u64")] #[serde(with = "serde_utils::quoted_u64")]
eth1_follow_distance: u64, eth1_follow_distance: u64,
#[serde(default = "default_subnets_per_node")]
#[serde(with = "serde_utils::quoted_u8")]
subnets_per_node: u8,
#[serde(with = "serde_utils::quoted_u64")] #[serde(with = "serde_utils::quoted_u64")]
inactivity_score_bias: u64, inactivity_score_bias: u64,
@ -1049,6 +1036,10 @@ fn default_safe_slots_to_import_optimistically() -> u64 {
128u64 128u64
} }
fn default_subnets_per_node() -> u8 {
2u8
}
impl Default for Config { impl Default for Config {
fn default() -> Self { fn default() -> Self {
let chain_spec = MainnetEthSpec::default_spec(); let chain_spec = MainnetEthSpec::default_spec();
@ -1135,6 +1126,7 @@ impl Config {
min_validator_withdrawability_delay: spec.min_validator_withdrawability_delay, min_validator_withdrawability_delay: spec.min_validator_withdrawability_delay,
shard_committee_period: spec.shard_committee_period, shard_committee_period: spec.shard_committee_period,
eth1_follow_distance: spec.eth1_follow_distance, eth1_follow_distance: spec.eth1_follow_distance,
subnets_per_node: spec.subnets_per_node,
inactivity_score_bias: spec.inactivity_score_bias, inactivity_score_bias: spec.inactivity_score_bias,
inactivity_score_recovery_rate: spec.inactivity_score_recovery_rate, inactivity_score_recovery_rate: spec.inactivity_score_recovery_rate,
@ -1183,6 +1175,7 @@ impl Config {
min_validator_withdrawability_delay, min_validator_withdrawability_delay,
shard_committee_period, shard_committee_period,
eth1_follow_distance, eth1_follow_distance,
subnets_per_node,
inactivity_score_bias, inactivity_score_bias,
inactivity_score_recovery_rate, inactivity_score_recovery_rate,
ejection_balance, ejection_balance,
@ -1217,6 +1210,7 @@ impl Config {
min_validator_withdrawability_delay, min_validator_withdrawability_delay,
shard_committee_period, shard_committee_period,
eth1_follow_distance, eth1_follow_distance,
subnets_per_node,
inactivity_score_bias, inactivity_score_bias,
inactivity_score_recovery_rate, inactivity_score_recovery_rate,
ejection_balance, ejection_balance,

View File

@ -87,10 +87,6 @@ pub fn get_extra_fields(spec: &ChainSpec) -> HashMap<String, Value> {
"domain_application_mask".to_uppercase()=> u32_hex(spec.domain_application_mask), "domain_application_mask".to_uppercase()=> u32_hex(spec.domain_application_mask),
"target_aggregators_per_committee".to_uppercase() => "target_aggregators_per_committee".to_uppercase() =>
spec.target_aggregators_per_committee.to_string().into(), spec.target_aggregators_per_committee.to_string().into(),
"random_subnets_per_validator".to_uppercase() =>
spec.random_subnets_per_validator.to_string().into(),
"epochs_per_random_subnet_subscription".to_uppercase() =>
spec.epochs_per_random_subnet_subscription.to_string().into(),
"domain_contribution_and_proof".to_uppercase() => "domain_contribution_and_proof".to_uppercase() =>
u32_hex(spec.domain_contribution_and_proof), u32_hex(spec.domain_contribution_and_proof),
"domain_sync_committee".to_uppercase() => u32_hex(spec.domain_sync_committee), "domain_sync_committee".to_uppercase() => u32_hex(spec.domain_sync_committee),

View File

@ -80,15 +80,26 @@ impl SubnetId {
epoch: Epoch, epoch: Epoch,
spec: &ChainSpec, spec: &ChainSpec,
) -> Result<(impl Iterator<Item = SubnetId>, Epoch), &'static str> { ) -> Result<(impl Iterator<Item = SubnetId>, Epoch), &'static str> {
// Simplify the variable name
let subscription_duration = spec.epochs_per_subnet_subscription;
let node_id_prefix = let node_id_prefix =
(node_id >> (256 - spec.attestation_subnet_prefix_bits() as usize)).as_usize(); (node_id >> (256 - spec.attestation_subnet_prefix_bits() as usize)).as_usize();
let subscription_event_idx = epoch.as_u64() / spec.epochs_per_subnet_subscription; // NOTE: The as_u64() panics if the number is larger than u64::max_value(). This cannot be
// true as spec.epochs_per_subnet_subscription is a u64.
let node_offset = (node_id % ethereum_types::U256::from(subscription_duration)).as_u64();
// Calculate at which epoch this node needs to re-evaluate
let valid_until_epoch = epoch.as_u64()
+ subscription_duration
.saturating_sub((epoch.as_u64() + node_offset) % subscription_duration);
let subscription_event_idx = (epoch.as_u64() + node_offset) / subscription_duration;
let permutation_seed = let permutation_seed =
ethereum_hashing::hash(&int_to_bytes::int_to_bytes8(subscription_event_idx)); ethereum_hashing::hash(&int_to_bytes::int_to_bytes8(subscription_event_idx));
let num_subnets = 1 << spec.attestation_subnet_prefix_bits(); let num_subnets = 1 << spec.attestation_subnet_prefix_bits();
let permutated_prefix = compute_shuffled_index( let permutated_prefix = compute_shuffled_index(
node_id_prefix, node_id_prefix,
num_subnets, num_subnets,
@ -107,7 +118,6 @@ impl SubnetId {
let subnet_set_generator = (0..subnets_per_node).map(move |idx| { let subnet_set_generator = (0..subnets_per_node).map(move |idx| {
SubnetId::new((permutated_prefix + idx as u64) % attestation_subnet_count) SubnetId::new((permutated_prefix + idx as u64) % attestation_subnet_count)
}); });
let valid_until_epoch = (subscription_event_idx + 1) * spec.epochs_per_subnet_subscription;
Ok((subnet_set_generator, valid_until_epoch.into())) Ok((subnet_set_generator, valid_until_epoch.into()))
} }
} }
@ -149,3 +159,80 @@ impl AsRef<str> for SubnetId {
subnet_id_to_string(self.0) subnet_id_to_string(self.0)
} }
} }
#[cfg(test)]
mod tests {
use super::*;
/// A set of tests compared to the python specification
#[test]
fn compute_subnets_for_epoch_unit_test() {
// Randomized variables used generated with the python specification
let node_ids = [
"0",
"88752428858350697756262172400162263450541348766581994718383409852729519486397",
"18732750322395381632951253735273868184515463718109267674920115648614659369468",
"27726842142488109545414954493849224833670205008410190955613662332153332462900",
"39755236029158558527862903296867805548949739810920318269566095185775868999998",
"31899136003441886988955119620035330314647133604576220223892254902004850516297",
"58579998103852084482416614330746509727562027284701078483890722833654510444626",
"28248042035542126088870192155378394518950310811868093527036637864276176517397",
"60930578857433095740782970114409273483106482059893286066493409689627770333527",
"103822458477361691467064888613019442068586830412598673713899771287914656699997",
]
.into_iter()
.map(|v| ethereum_types::U256::from_dec_str(v).unwrap())
.collect::<Vec<_>>();
let epochs = [
54321u64, 1017090249, 1827566880, 846255942, 766597383, 1204990115, 1616209495,
1774367616, 1484598751, 3525502229,
]
.into_iter()
.map(Epoch::from)
.collect::<Vec<_>>();
// Test mainnet
let spec = ChainSpec::mainnet();
// Calculated by hand
let expected_valid_time: Vec<u64> = [
54528, 1017090371, 1827567108, 846256076, 766597570, 1204990135, 1616209582,
1774367723, 1484598953, 3525502371,
]
.into();
// Calculated from pyspec
let expected_subnets = vec![
vec![4u64, 5u64],
vec![61, 62],
vec![23, 24],
vec![38, 39],
vec![53, 54],
vec![39, 40],
vec![48, 49],
vec![39, 40],
vec![34, 35],
vec![37, 38],
];
for x in 0..node_ids.len() {
println!("Test: {}", x);
println!(
"NodeId: {}\n Epoch: {}\n, expected_update_time: {}\n, expected_subnets: {:?}",
node_ids[x], epochs[x], expected_valid_time[x], expected_subnets[x]
);
let (computed_subnets, valid_time) = SubnetId::compute_subnets_for_epoch::<
crate::MainnetEthSpec,
>(node_ids[x], epochs[x], &spec)
.unwrap();
assert_eq!(Epoch::from(expected_valid_time[x]), valid_time);
assert_eq!(
expected_subnets[x],
computed_subnets.map(SubnetId::into).collect::<Vec<u64>>()
);
}
}
}

View File

@ -6,6 +6,9 @@ use beacon_node::{get_data_dir, get_slots_per_restore_point, ClientConfig};
use clap::{App, Arg, ArgMatches}; use clap::{App, Arg, ArgMatches};
use environment::{Environment, RuntimeContext}; use environment::{Environment, RuntimeContext};
use slog::{info, Logger}; use slog::{info, Logger};
use std::fs;
use std::io::Write;
use std::path::PathBuf;
use store::{ use store::{
errors::Error, errors::Error,
metadata::{SchemaVersion, CURRENT_SCHEMA_VERSION}, metadata::{SchemaVersion, CURRENT_SCHEMA_VERSION},
@ -57,6 +60,13 @@ pub fn inspect_cli_app<'a, 'b>() -> App<'a, 'b> {
.default_value("sizes") .default_value("sizes")
.possible_values(InspectTarget::VARIANTS), .possible_values(InspectTarget::VARIANTS),
) )
.arg(
Arg::with_name("output-dir")
.long("output-dir")
.value_name("DIR")
.help("Base directory for the output files. Defaults to the current directory")
.takes_value(true),
)
} }
pub fn prune_payloads_app<'a, 'b>() -> App<'a, 'b> { pub fn prune_payloads_app<'a, 'b>() -> App<'a, 'b> {
@ -190,18 +200,27 @@ pub enum InspectTarget {
ValueSizes, ValueSizes,
#[strum(serialize = "total")] #[strum(serialize = "total")]
ValueTotal, ValueTotal,
#[strum(serialize = "values")]
Values,
} }
pub struct InspectConfig { pub struct InspectConfig {
column: DBColumn, column: DBColumn,
target: InspectTarget, target: InspectTarget,
/// Configures where the inspect output should be stored.
output_dir: PathBuf,
} }
fn parse_inspect_config(cli_args: &ArgMatches) -> Result<InspectConfig, String> { fn parse_inspect_config(cli_args: &ArgMatches) -> Result<InspectConfig, String> {
let column = clap_utils::parse_required(cli_args, "column")?; let column = clap_utils::parse_required(cli_args, "column")?;
let target = clap_utils::parse_required(cli_args, "output")?; let target = clap_utils::parse_required(cli_args, "output")?;
let output_dir: PathBuf =
Ok(InspectConfig { column, target }) clap_utils::parse_optional(cli_args, "output-dir")?.unwrap_or_else(PathBuf::new);
Ok(InspectConfig {
column,
target,
output_dir,
})
} }
pub fn inspect_db<E: EthSpec>( pub fn inspect_db<E: EthSpec>(
@ -209,7 +228,7 @@ pub fn inspect_db<E: EthSpec>(
client_config: ClientConfig, client_config: ClientConfig,
runtime_context: &RuntimeContext<E>, runtime_context: &RuntimeContext<E>,
log: Logger, log: Logger,
) -> Result<(), Error> { ) -> Result<(), String> {
let spec = runtime_context.eth2_config.spec.clone(); let spec = runtime_context.eth2_config.spec.clone();
let hot_path = client_config.get_db_path(); let hot_path = client_config.get_db_path();
let cold_path = client_config.get_freezer_db_path(); let cold_path = client_config.get_freezer_db_path();
@ -223,12 +242,19 @@ pub fn inspect_db<E: EthSpec>(
client_config.store, client_config.store,
spec, spec,
log, log,
)?; )
.map_err(|e| format!("{:?}", e))?;
let mut total = 0; let mut total = 0;
let base_path = &inspect_config.output_dir;
if let InspectTarget::Values = inspect_config.target {
fs::create_dir_all(base_path)
.map_err(|e| format!("Unable to create import directory: {:?}", e))?;
}
for res in db.hot_db.iter_column(inspect_config.column) { for res in db.hot_db.iter_column(inspect_config.column) {
let (key, value) = res?; let (key, value) = res.map_err(|e| format!("{:?}", e))?;
match inspect_config.target { match inspect_config.target {
InspectTarget::ValueSizes => { InspectTarget::ValueSizes => {
@ -238,11 +264,32 @@ pub fn inspect_db<E: EthSpec>(
InspectTarget::ValueTotal => { InspectTarget::ValueTotal => {
total += value.len(); total += value.len();
} }
InspectTarget::Values => {
let file_path =
base_path.join(format!("{}_{}.ssz", inspect_config.column.as_str(), key));
let write_result = fs::OpenOptions::new()
.create(true)
.write(true)
.open(&file_path)
.map_err(|e| format!("Failed to open file: {:?}", e))
.map(|mut file| {
file.write_all(&value)
.map_err(|e| format!("Failed to write file: {:?}", e))
});
if let Err(e) = write_result {
println!("Error writing values to file {:?}: {:?}", file_path, e);
} else {
println!("Successfully saved values to file: {:?}", file_path);
}
total += value.len();
}
} }
} }
match inspect_config.target { match inspect_config.target {
InspectTarget::ValueSizes | InspectTarget::ValueTotal => { InspectTarget::ValueSizes | InspectTarget::ValueTotal | InspectTarget::Values => {
println!("Total: {} bytes", total); println!("Total: {} bytes", total);
} }
} }
@ -359,22 +406,24 @@ pub fn run<T: EthSpec>(cli_args: &ArgMatches<'_>, env: Environment<T>) -> Result
let client_config = parse_client_config(cli_args, &env)?; let client_config = parse_client_config(cli_args, &env)?;
let context = env.core_context(); let context = env.core_context();
let log = context.log().clone(); let log = context.log().clone();
let format_err = |e| format!("Fatal error: {:?}", e);
match cli_args.subcommand() { match cli_args.subcommand() {
("version", Some(_)) => display_db_version(client_config, &context, log), ("version", Some(_)) => {
display_db_version(client_config, &context, log).map_err(format_err)
}
("migrate", Some(cli_args)) => { ("migrate", Some(cli_args)) => {
let migrate_config = parse_migrate_config(cli_args)?; let migrate_config = parse_migrate_config(cli_args)?;
migrate_db(migrate_config, client_config, &context, log) migrate_db(migrate_config, client_config, &context, log).map_err(format_err)
} }
("inspect", Some(cli_args)) => { ("inspect", Some(cli_args)) => {
let inspect_config = parse_inspect_config(cli_args)?; let inspect_config = parse_inspect_config(cli_args)?;
inspect_db(inspect_config, client_config, &context, log) inspect_db(inspect_config, client_config, &context, log)
} }
("prune_payloads", Some(_)) => prune_payloads(client_config, &context, log), ("prune_payloads", Some(_)) => {
prune_payloads(client_config, &context, log).map_err(format_err)
}
("prune_blobs", Some(_)) => prune_blobs(client_config, &context, log), ("prune_blobs", Some(_)) => prune_blobs(client_config, &context, log),
_ => { _ => Err("Unknown subcommand, for help `lighthouse database_manager --help`".into()),
return Err("Unknown subcommand, for help `lighthouse database_manager --help`".into())
} }
}
.map_err(|e| format!("Fatal error: {:?}", e))
} }

View File

@ -1451,6 +1451,26 @@ fn empty_self_limiter_flag() {
) )
}); });
} }
#[test]
fn empty_inbound_rate_limiter_flag() {
CommandLineTest::new()
.run_with_zero_port()
.with_config(|config| {
assert_eq!(
config.network.inbound_rate_limiter_config,
Some(lighthouse_network::rpc::config::InboundRateLimiterConfig::default())
)
});
}
#[test]
fn disable_inbound_rate_limiter_flag() {
CommandLineTest::new()
.flag("inbound-rate-limiter", Some("disabled"))
.run_with_zero_port()
.with_config(|config| assert_eq!(config.network.inbound_rate_limiter_config, None));
}
#[test] #[test]
fn http_allow_origin_flag() { fn http_allow_origin_flag() {
CommandLineTest::new() CommandLineTest::new()

View File

@ -1,109 +0,0 @@
#!/usr/bin/env bash
# Based on: https://github.com/tokio-rs/tokio/blob/master/bin/publish
set -e
USAGE="Publish a new release of a lighthouse crate
USAGE:
$(basename "$0") [OPTIONS] [CRATE_PATH] [CRATE] [TAG_NAME]
OPTIONS:
-v, --verbose Use verbose Cargo output
-d, --dry-run Perform a dry run (do not publish the release)
-h, --help Show this help text and exit
--allow-dirty Allow dirty working directories to be packaged"
DRY_RUN=""
DIRTY=""
VERBOSE=""
verify() {
echo "Verifying if $CRATE v$VERSION can be released"
# `cargo pkgid` has different formats based on whether the `[lib]` name and `[package]` name
# are the same, necessitating the following logic.
#
# Try to match on `#`
ACTUAL=$(cargo pkgid | sed -n 's/.*#\([0-9]\)/\1/p' )
if [ -z "$ACTUAL" ]; then
# Match on the final `:`
ACTUAL=$(cargo pkgid | sed -n 's/.*:\(.*\)/\1/p')
fi
if [ "$ACTUAL" != "$VERSION" ]; then
echo "expected to release version $VERSION, but Cargo.toml contained $ACTUAL"
exit 1
fi
}
release() {
echo "Releasing $CRATE v$VERSION"
cargo package $VERBOSE $DIRTY
cargo publish $VERBOSE $DRY_RUN $DIRTY
}
while [[ $# -gt 0 ]]
do
case "$1" in
-h|--help)
echo "$USAGE"
exit 0
;;
-v|--verbose)
VERBOSE="--verbose"
set +x
shift
;;
--allow-dirty)
DIRTY="--allow-dirty"
shift
;;
-d|--dry-run)
DRY_RUN="--dry-run"
shift
;;
-*)
echo "unknown flag \"$1\""
echo "$USAGE"
exit 1
;;
*) # crate, crate path, or version
if [ -z "$CRATE_PATH" ]; then
CRATE_PATH="$1"
elif [ -z "$CRATE" ]; then
CRATE="$1"
elif [ -z "$TAG_NAME" ]; then
TAG_NAME="$1"
VERSION=$(sed -e 's#.*-v\([0-9]\)#\1#' <<< "$TAG_NAME")
else
echo "unknown positional argument \"$1\""
echo "$USAGE"
exit 1
fi
shift
;;
esac
done
# set -- "${POSITIONAL[@]}"
if [ -z "$VERSION" ]; then
echo "no version specified!"
HELP=1
fi
if [ -z "$CRATE" ]; then
echo "no crate specified!"
HELP=1
fi
if [ -n "$HELP" ]; then
echo "$USAGE"
exit 1
fi
if [ -d "$CRATE_PATH" ]; then
(cd "$CRATE_PATH" && verify && release )
else
echo "no such dir \"$CRATE_PATH\""
exit 1
fi

View File

@ -14,7 +14,8 @@
"mergeNetsplitBlock": 0, "mergeNetsplitBlock": 0,
"shanghaiTime": 0, "shanghaiTime": 0,
"shardingForkTime": 0, "shardingForkTime": 0,
"terminalTotalDifficulty": 0 "terminalTotalDifficulty": 0,
"terminalTotalDifficultyPassed": true
}, },
"alloc": { "alloc": {
"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": { "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": {

View File

@ -14,7 +14,8 @@
"mergeForkBlock": 0, "mergeForkBlock": 0,
"shanghaiTime": 0, "shanghaiTime": 0,
"shardingForkTime": 0, "shardingForkTime": 0,
"terminalTotalDifficulty": 0 "terminalTotalDifficulty": 0,
"terminalTotalDifficultyPassed": true
}, },
"alloc": { "alloc": {
"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": { "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": {

View File

@ -11,7 +11,7 @@ use unused_port::unused_tcp4_port;
/// We've pinned the Nethermind version since our method of using the `master` branch to /// We've pinned the Nethermind version since our method of using the `master` branch to
/// find the latest tag isn't working. It appears Nethermind don't always tag on `master`. /// find the latest tag isn't working. It appears Nethermind don't always tag on `master`.
/// We should fix this so we always pull the latest version of Nethermind. /// We should fix this so we always pull the latest version of Nethermind.
const NETHERMIND_BRANCH: &str = "release/1.17.1"; const NETHERMIND_BRANCH: &str = "release/1.18.2";
const NETHERMIND_REPO_URL: &str = "https://github.com/NethermindEth/nethermind"; const NETHERMIND_REPO_URL: &str = "https://github.com/NethermindEth/nethermind";
fn build_result(repo_dir: &Path) -> Output { fn build_result(repo_dir: &Path) -> Output {

View File

@ -146,7 +146,7 @@ impl<T: EthSpec> ProductionValidatorClient<T> {
context context
.clone() .clone()
.executor .executor
.spawn_without_exit(async move { server.await }, "metrics-api"); .spawn_without_exit(server, "metrics-api");
Some(ctx) Some(ctx)
} else { } else {
@ -590,7 +590,7 @@ impl<T: EthSpec> ProductionValidatorClient<T> {
self.context self.context
.clone() .clone()
.executor .executor
.spawn_without_exit(async move { server.await }, "http-api"); .spawn_without_exit(server, "http-api");
Some(listen_addr) Some(listen_addr)
} else { } else {

View File

@ -12,7 +12,7 @@ data which is:
### Requirements ### Requirements
- `git` - `git`
- `rust` : https://rustup.rs/ - `rust` : https://rustup.rs/
- `libpg` : https://www.postgresql.org/download/ - `libpq` : https://www.postgresql.org/download/
- `diesel_cli` : - `diesel_cli` :
``` ```
cargo install diesel_cli --no-default-features --features postgres cargo install diesel_cli --no-default-features --features postgres