Resolve merge conflicts

This commit is contained in:
Eitan Seri- Levi
2026-03-16 02:24:50 -07:00
270 changed files with 16594 additions and 8798 deletions

View File

@@ -6,7 +6,7 @@ edition = { workspace = true }
[features]
default = []
arbitrary-fuzz = ["dep:arbitrary", "types/arbitrary"]
arbitrary = ["dep:arbitrary", "types/arbitrary"]
json = ["dep:serde_json"]
[dependencies]

View File

@@ -13,7 +13,7 @@ pub enum Error {
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
pub struct InterchangeMetadata {
#[serde(with = "serde_utils::quoted_u64::require_quotes")]
pub interchange_format_version: u64,
@@ -22,7 +22,7 @@ pub struct InterchangeMetadata {
#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
pub struct InterchangeData {
pub pubkey: PublicKeyBytes,
pub signed_blocks: Vec<SignedBlock>,
@@ -31,7 +31,7 @@ pub struct InterchangeData {
#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
pub struct SignedBlock {
#[serde(with = "serde_utils::quoted_u64::require_quotes")]
pub slot: Slot,
@@ -41,7 +41,7 @@ pub struct SignedBlock {
#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
pub struct SignedAttestation {
#[serde(with = "serde_utils::quoted_u64::require_quotes")]
pub source_epoch: Epoch,
@@ -52,7 +52,7 @@ pub struct SignedAttestation {
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
pub struct Interchange {
pub metadata: InterchangeMetadata,
pub data: Vec<InterchangeData>,

View File

@@ -8,19 +8,23 @@ edition = { workspace = true }
default = []
lighthouse = ["proto_array", "eth2_keystore", "eip_3076", "zeroize"]
events = ["reqwest-eventsource", "futures", "futures-util"]
network = ["libp2p-identity", "enr", "multiaddr"]
[dependencies]
bls = { workspace = true }
context_deserialize = { workspace = true }
educe = { workspace = true }
eip_3076 = { workspace = true, optional = true }
enr = { version = "0.13.0", features = ["ed25519"], optional = true }
eth2_keystore = { workspace = true, optional = true }
ethereum_serde_utils = { workspace = true }
ethereum_ssz = { workspace = true }
ethereum_ssz_derive = { workspace = true }
futures = { workspace = true, optional = true }
futures-util = { version = "0.3.8", optional = true }
libp2p-identity = { version = "0.2", features = ["peerid"], optional = true }
mediatype = "0.19.13"
multiaddr = { version = "0.18.2", optional = true }
pretty_reqwest_error = { workspace = true }
proto_array = { workspace = true, optional = true }
reqwest = { workspace = true }

View File

@@ -102,6 +102,7 @@ impl Error {
None
}
}
#[cfg(feature = "events")]
Error::SseEventSource(_) => None,
Error::ServerMessage(msg) => StatusCode::try_from(msg.code).ok(),
Error::ServerIndexedMessage(msg) => StatusCode::try_from(msg.code).ok(),

View File

@@ -22,8 +22,6 @@ pub use beacon_response::{
};
pub use self::error::{Error, ok_or_error, success_or_error};
pub use reqwest;
pub use reqwest::{StatusCode, Url};
pub use sensitive_url::SensitiveUrl;
use self::mixin::{RequestAccept, ResponseOptional};
@@ -35,14 +33,16 @@ use educe::Educe;
use futures::Stream;
#[cfg(feature = "events")]
use futures_util::StreamExt;
#[cfg(feature = "network")]
use libp2p_identity::PeerId;
use reqwest::{
Body, IntoUrl, RequestBuilder, Response,
Body, IntoUrl, RequestBuilder, Response, StatusCode, Url,
header::{HeaderMap, HeaderValue},
};
#[cfg(feature = "events")]
use reqwest_eventsource::{Event, RequestBuilderExt};
use serde::{Serialize, de::DeserializeOwned};
use ssz::Encode;
use ssz::{Decode, Encode};
use std::fmt;
use std::future::Future;
use std::time::Duration;
@@ -50,6 +50,7 @@ use std::time::Duration;
pub const V1: EndpointVersion = EndpointVersion(1);
pub const V2: EndpointVersion = EndpointVersion(2);
pub const V3: EndpointVersion = EndpointVersion(3);
pub const V4: EndpointVersion = EndpointVersion(4);
pub const CONSENSUS_VERSION_HEADER: &str = "Eth-Consensus-Version";
pub const EXECUTION_PAYLOAD_BLINDED_HEADER: &str = "Eth-Execution-Payload-Blinded";
@@ -76,8 +77,6 @@ const HTTP_GET_BEACON_BLOCK_SSZ_TIMEOUT_QUOTIENT: u32 = 4;
const HTTP_GET_DEBUG_BEACON_STATE_QUOTIENT: u32 = 4;
const HTTP_GET_DEPOSIT_SNAPSHOT_QUOTIENT: u32 = 4;
const HTTP_GET_VALIDATOR_BLOCK_TIMEOUT_QUOTIENT: u32 = 4;
// Generally the timeout for events should be longer than a slot.
const HTTP_GET_EVENTS_TIMEOUT_MULTIPLIER: u32 = 50;
const HTTP_DEFAULT_TIMEOUT_QUOTIENT: u32 = 4;
/// A struct to define a variety of different timeouts for different validator tasks to ensure
@@ -98,7 +97,6 @@ pub struct Timeouts {
pub get_debug_beacon_states: Duration,
pub get_deposit_snapshot: Duration,
pub get_validator_block: Duration,
pub events: Duration,
pub default: Duration,
}
@@ -119,7 +117,6 @@ impl Timeouts {
get_debug_beacon_states: timeout,
get_deposit_snapshot: timeout,
get_validator_block: timeout,
events: HTTP_GET_EVENTS_TIMEOUT_MULTIPLIER * timeout,
default: timeout,
}
}
@@ -142,7 +139,6 @@ impl Timeouts {
get_debug_beacon_states: base_timeout / HTTP_GET_DEBUG_BEACON_STATE_QUOTIENT,
get_deposit_snapshot: base_timeout / HTTP_GET_DEPOSIT_SNAPSHOT_QUOTIENT,
get_validator_block: base_timeout / HTTP_GET_VALIDATOR_BLOCK_TIMEOUT_QUOTIENT,
events: HTTP_GET_EVENTS_TIMEOUT_MULTIPLIER * base_timeout,
default: base_timeout / HTTP_DEFAULT_TIMEOUT_QUOTIENT,
}
}
@@ -902,6 +898,47 @@ impl BeaconNodeHttpClient {
.map(|opt| opt.map(BeaconResponse::ForkVersioned))
}
/// `GET beacon/states/{state_id}/proposer_lookahead`
///
/// Returns `Ok(None)` on a 404 error.
pub async fn get_beacon_states_proposer_lookahead(
&self,
state_id: StateId,
) -> Result<Option<ExecutionOptimisticFinalizedBeaconResponse<ValidatorIndexData>>, Error> {
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
.push("beacon")
.push("states")
.push(&state_id.to_string())
.push("proposer_lookahead");
self.get_fork_contextual(path, |fork| fork)
.await
.map(|opt| opt.map(BeaconResponse::ForkVersioned))
}
/// `GET beacon/states/{state_id}/proposer_lookahead`
///
/// Returns `Ok(None)` on a 404 error.
pub async fn get_beacon_states_proposer_lookahead_ssz(
&self,
state_id: StateId,
) -> Result<Option<Vec<u8>>, Error> {
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
.push("beacon")
.push("states")
.push(&state_id.to_string())
.push("proposer_lookahead");
self.get_bytes_opt_accept_header(path, Accept::Ssz, self.timeouts.default)
.await
}
/// `GET beacon/light_client/updates`
///
/// Returns `Ok(None)` on a 404 error.
@@ -1765,7 +1802,7 @@ impl BeaconNodeHttpClient {
&self,
block_id: BlockId,
validators: &[ValidatorId],
) -> Result<GenericResponse<Vec<SyncCommitteeReward>>, Error> {
) -> Result<ExecutionOptimisticFinalizedResponse<Vec<SyncCommitteeReward>>, Error> {
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
@@ -1782,7 +1819,7 @@ impl BeaconNodeHttpClient {
pub async fn get_beacon_rewards_blocks(
&self,
block_id: BlockId,
) -> Result<GenericResponse<StandardBlockReward>, Error> {
) -> Result<ExecutionOptimisticFinalizedResponse<StandardBlockReward>, Error> {
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
@@ -1800,7 +1837,7 @@ impl BeaconNodeHttpClient {
&self,
epoch: Epoch,
validators: &[ValidatorId],
) -> Result<StandardAttestationRewards, Error> {
) -> Result<ExecutionOptimisticFinalizedResponse<StandardAttestationRewards>, Error> {
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
@@ -1939,6 +1976,7 @@ impl BeaconNodeHttpClient {
}
/// `GET node/identity`
#[cfg(feature = "network")]
pub async fn get_node_identity(&self) -> Result<GenericResponse<IdentityData>, Error> {
let mut path = self.eth_path(V1)?;
@@ -1986,9 +2024,10 @@ impl BeaconNodeHttpClient {
}
/// `GET node/peers/{peer_id}`
#[cfg(feature = "network")]
pub async fn get_node_peers_by_id(
&self,
peer_id: &str,
peer_id: PeerId,
) -> Result<GenericResponse<PeerData>, Error> {
let mut path = self.eth_path(V1)?;
@@ -1996,7 +2035,7 @@ impl BeaconNodeHttpClient {
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
.push("node")
.push("peers")
.push(peer_id);
.push(&peer_id.to_string());
self.get(path).await
}
@@ -2146,6 +2185,24 @@ impl BeaconNodeHttpClient {
.await
}
/// `GET v2/validator/duties/proposer/{epoch}`
pub async fn get_validator_duties_proposer_v2(
&self,
epoch: Epoch,
) -> Result<DutiesResponse<Vec<ProposerData>>, Error> {
let mut path = self.eth_path(V2)?;
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
.push("validator")
.push("duties")
.push("proposer")
.push(&epoch.to_string());
self.get_with_timeout(path, self.timeouts.proposer_duties)
.await
}
/// `GET v2/validator/blocks/{slot}`
pub async fn get_validator_blocks<E: EthSpec>(
&self,
@@ -2404,6 +2461,277 @@ impl BeaconNodeHttpClient {
opt_response.ok_or(Error::StatusCode(StatusCode::NOT_FOUND))
}
/// returns `GET v4/validator/blocks/{slot}` URL path
pub async fn get_validator_blocks_v4_path(
&self,
slot: Slot,
randao_reveal: &SignatureBytes,
graffiti: Option<&Graffiti>,
skip_randao_verification: SkipRandaoVerification,
builder_booster_factor: Option<u64>,
graffiti_policy: Option<GraffitiPolicy>,
) -> Result<Url, Error> {
let mut path = self.eth_path(V4)?;
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
.push("validator")
.push("blocks")
.push(&slot.to_string());
path.query_pairs_mut()
.append_pair("randao_reveal", &randao_reveal.to_string());
if let Some(graffiti) = graffiti {
path.query_pairs_mut()
.append_pair("graffiti", &graffiti.to_string());
}
if skip_randao_verification == SkipRandaoVerification::Yes {
path.query_pairs_mut()
.append_pair("skip_randao_verification", "");
}
if let Some(builder_booster_factor) = builder_booster_factor {
path.query_pairs_mut()
.append_pair("builder_boost_factor", &builder_booster_factor.to_string());
}
if let Some(GraffitiPolicy::AppendClientVersions) = graffiti_policy {
path.query_pairs_mut()
.append_pair("graffiti_policy", "AppendClientVersions");
}
Ok(path)
}
/// `GET v4/validator/blocks/{slot}`
pub async fn get_validator_blocks_v4<E: EthSpec>(
&self,
slot: Slot,
randao_reveal: &SignatureBytes,
graffiti: Option<&Graffiti>,
builder_booster_factor: Option<u64>,
graffiti_policy: Option<GraffitiPolicy>,
) -> Result<
(
ForkVersionedResponse<BeaconBlock<E>, ProduceBlockV4Metadata>,
ProduceBlockV4Metadata,
),
Error,
> {
self.get_validator_blocks_v4_modular(
slot,
randao_reveal,
graffiti,
SkipRandaoVerification::No,
builder_booster_factor,
graffiti_policy,
)
.await
}
/// `GET v4/validator/blocks/{slot}`
pub async fn get_validator_blocks_v4_modular<E: EthSpec>(
&self,
slot: Slot,
randao_reveal: &SignatureBytes,
graffiti: Option<&Graffiti>,
skip_randao_verification: SkipRandaoVerification,
builder_booster_factor: Option<u64>,
graffiti_policy: Option<GraffitiPolicy>,
) -> Result<
(
ForkVersionedResponse<BeaconBlock<E>, ProduceBlockV4Metadata>,
ProduceBlockV4Metadata,
),
Error,
> {
let path = self
.get_validator_blocks_v4_path(
slot,
randao_reveal,
graffiti,
skip_randao_verification,
builder_booster_factor,
graffiti_policy,
)
.await?;
let opt_result = self
.get_response_with_response_headers(
path,
Accept::Json,
self.timeouts.get_validator_block,
|response, headers| async move {
let header_metadata = ProduceBlockV4Metadata::try_from(&headers)
.map_err(Error::InvalidHeaders)?;
let block_response = response
.json::<ForkVersionedResponse<BeaconBlock<E>, ProduceBlockV4Metadata>>()
.await?;
Ok((block_response, header_metadata))
},
)
.await?;
opt_result.ok_or(Error::StatusCode(StatusCode::NOT_FOUND))
}
/// `GET v4/validator/blocks/{slot}` in ssz format
pub async fn get_validator_blocks_v4_ssz<E: EthSpec>(
&self,
slot: Slot,
randao_reveal: &SignatureBytes,
graffiti: Option<&Graffiti>,
builder_booster_factor: Option<u64>,
graffiti_policy: Option<GraffitiPolicy>,
) -> Result<(BeaconBlock<E>, ProduceBlockV4Metadata), Error> {
self.get_validator_blocks_v4_modular_ssz::<E>(
slot,
randao_reveal,
graffiti,
SkipRandaoVerification::No,
builder_booster_factor,
graffiti_policy,
)
.await
}
/// `GET v4/validator/blocks/{slot}` in ssz format
pub async fn get_validator_blocks_v4_modular_ssz<E: EthSpec>(
&self,
slot: Slot,
randao_reveal: &SignatureBytes,
graffiti: Option<&Graffiti>,
skip_randao_verification: SkipRandaoVerification,
builder_booster_factor: Option<u64>,
graffiti_policy: Option<GraffitiPolicy>,
) -> Result<(BeaconBlock<E>, ProduceBlockV4Metadata), Error> {
let path = self
.get_validator_blocks_v4_path(
slot,
randao_reveal,
graffiti,
skip_randao_verification,
builder_booster_factor,
graffiti_policy,
)
.await?;
let opt_response = self
.get_response_with_response_headers(
path,
Accept::Ssz,
self.timeouts.get_validator_block,
|response, headers| async move {
let metadata = ProduceBlockV4Metadata::try_from(&headers)
.map_err(Error::InvalidHeaders)?;
let response_bytes = response.bytes().await?;
let block = BeaconBlock::from_ssz_bytes_for_fork(
&response_bytes,
metadata.consensus_version,
)
.map_err(Error::InvalidSsz)?;
Ok((block, metadata))
},
)
.await?;
opt_response.ok_or(Error::StatusCode(StatusCode::NOT_FOUND))
}
/// `GET v1/validator/execution_payload_envelope/{slot}/{builder_index}`
pub async fn get_validator_execution_payload_envelope<E: EthSpec>(
&self,
slot: Slot,
builder_index: u64,
) -> Result<ForkVersionedResponse<ExecutionPayloadEnvelope<E>>, Error> {
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
.push("validator")
.push("execution_payload_envelope")
.push(&slot.to_string())
.push(&builder_index.to_string());
self.get(path).await
}
/// `GET v1/validator/execution_payload_envelope/{slot}/{builder_index}` in SSZ format
pub async fn get_validator_execution_payload_envelope_ssz<E: EthSpec>(
&self,
slot: Slot,
builder_index: u64,
) -> Result<ExecutionPayloadEnvelope<E>, Error> {
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
.push("validator")
.push("execution_payload_envelope")
.push(&slot.to_string())
.push(&builder_index.to_string());
let opt_response = self
.get_bytes_opt_accept_header(path, Accept::Ssz, self.timeouts.get_validator_block)
.await?;
let response_bytes = opt_response.ok_or(Error::StatusCode(StatusCode::NOT_FOUND))?;
ExecutionPayloadEnvelope::from_ssz_bytes(&response_bytes).map_err(Error::InvalidSsz)
}
/// `POST v1/beacon/execution_payload_envelope`
pub async fn post_beacon_execution_payload_envelope<E: EthSpec>(
&self,
envelope: &SignedExecutionPayloadEnvelope<E>,
fork_name: ForkName,
) -> Result<(), Error> {
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
.push("beacon")
.push("execution_payload_envelope");
self.post_generic_with_consensus_version(
path,
envelope,
Some(self.timeouts.proposal),
fork_name,
)
.await?;
Ok(())
}
/// `POST v1/beacon/execution_payload_envelope` in SSZ format
pub async fn post_beacon_execution_payload_envelope_ssz<E: EthSpec>(
&self,
envelope: &SignedExecutionPayloadEnvelope<E>,
fork_name: ForkName,
) -> Result<(), Error> {
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
.push("beacon")
.push("execution_payload_envelope");
self.post_generic_with_consensus_version_and_ssz_body(
path,
envelope.as_ssz_bytes(),
Some(self.timeouts.proposal),
fork_name,
)
.await?;
Ok(())
}
/// `GET v2/validator/blocks/{slot}` in ssz format
pub async fn get_validator_blocks_ssz<E: EthSpec>(
&self,
@@ -2805,10 +3133,14 @@ impl BeaconNodeHttpClient {
.join(",");
path.query_pairs_mut().append_pair("topics", &topic_string);
// Do not use a timeout for the events endpoint. Using a regular timeout will trigger a
// timeout every `timeout` seconds, regardless of any data streamed from the endpoint.
// In future we could add a read_timeout, but that can only be configured globally on the
// Client.
let mut es = self
.client
.get(path)
.timeout(self.timeouts.events)
.timeout(Duration::MAX)
.eventsource()
.map_err(Error::SseEventSource)?;
// If we don't await `Event::Open` here, then the consumer

View File

@@ -2,12 +2,11 @@
mod attestation_performance;
mod block_packing_efficiency;
mod block_rewards;
mod custody;
pub mod sync_state;
use crate::{
BeaconNodeHttpClient, DepositData, Error, Hash256, Slot,
BeaconNodeHttpClient, DepositData, Error, Hash256,
lighthouse::sync_state::SyncState,
types::{AdminPeer, Epoch, GenericResponse, ValidatorId},
};
@@ -22,7 +21,6 @@ pub use attestation_performance::{
pub use block_packing_efficiency::{
BlockPackingEfficiency, BlockPackingEfficiencyQuery, ProposerInfo, UniqueAttestation,
};
pub use block_rewards::{AttestationRewards, BlockReward, BlockRewardMeta, BlockRewardsQuery};
pub use custody::CustodyInfo;
// Define "legacy" implementations of `Option<T>` which use four bytes for encoding the union
@@ -317,27 +315,6 @@ impl BeaconNodeHttpClient {
Analysis endpoints.
*/
/// `GET` lighthouse/analysis/block_rewards?start_slot,end_slot
pub async fn get_lighthouse_analysis_block_rewards(
&self,
start_slot: Slot,
end_slot: Slot,
) -> Result<Vec<BlockReward>, Error> {
let mut path = self.server.expose_full().clone();
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
.push("lighthouse")
.push("analysis")
.push("block_rewards");
path.query_pairs_mut()
.append_pair("start_slot", &start_slot.to_string())
.append_pair("end_slot", &end_slot.to_string());
self.get(path).await
}
/// `GET` lighthouse/analysis/block_packing?start_epoch,end_epoch
pub async fn get_lighthouse_analysis_block_packing(
&self,

View File

@@ -1,60 +0,0 @@
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use types::{AttestationData, Hash256, Slot};
/// Details about the rewards paid to a block proposer for proposing a block.
///
/// All rewards in GWei.
///
/// Presently this only counts attestation rewards, but in future should be expanded
/// to include information on slashings and sync committee aggregates too.
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
pub struct BlockReward {
/// Sum of all reward components.
pub total: u64,
/// Block root of the block that these rewards are for.
pub block_root: Hash256,
/// Metadata about the block, particularly reward-relevant metadata.
pub meta: BlockRewardMeta,
/// Rewards due to attestations.
pub attestation_rewards: AttestationRewards,
/// Sum of rewards due to sync committee signatures.
pub sync_committee_rewards: u64,
}
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
pub struct BlockRewardMeta {
pub slot: Slot,
pub parent_slot: Slot,
pub proposer_index: u64,
pub graffiti: String,
}
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
pub struct AttestationRewards {
/// Total block reward from attestations included.
pub total: u64,
/// Total rewards from previous epoch attestations.
pub prev_epoch_total: u64,
/// Total rewards from current epoch attestations.
pub curr_epoch_total: u64,
/// Vec of attestation rewards for each attestation included.
///
/// Each element of the vec is a map from validator index to reward.
pub per_attestation_rewards: Vec<HashMap<u64, u64>>,
/// The attestations themselves (optional).
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub attestations: Vec<AttestationData>,
}
/// Query parameters for the `/lighthouse/block_rewards` endpoint.
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
pub struct BlockRewardsQuery {
/// Lower slot limit for block rewards returned (inclusive).
pub start_slot: Slot,
/// Upper slot limit for block rewards returned (inclusive).
pub end_slot: Slot,
/// Include the full attestations themselves?
#[serde(default)]
pub include_attestations: bool,
}

View File

@@ -9,7 +9,11 @@ use crate::{
};
use bls::{PublicKeyBytes, SecretKey, Signature, SignatureBytes};
use context_deserialize::ContextDeserialize;
#[cfg(feature = "network")]
use enr::{CombinedKey, Enr};
use mediatype::{MediaType, MediaTypeList, names};
#[cfg(feature = "network")]
use multiaddr::Multiaddr;
use reqwest::header::HeaderMap;
use serde::{Deserialize, Deserializer, Serialize};
use serde_utils::quoted_u64::Quoted;
@@ -33,9 +37,6 @@ pub mod beacon_response {
pub use crate::beacon_response::*;
}
#[cfg(feature = "lighthouse")]
use crate::lighthouse::BlockReward;
// Re-export error types from the unified error module
pub use crate::error::{ErrorMessage, Failure, IndexedErrorMessage, ResponseError as Error};
@@ -559,12 +560,13 @@ pub struct ChainHeadData {
pub execution_optimistic: Option<bool>,
}
#[cfg(feature = "network")]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct IdentityData {
pub peer_id: String,
pub enr: String,
pub p2p_addresses: Vec<String>,
pub discovery_addresses: Vec<String>,
pub enr: Enr<CombinedKey>,
pub p2p_addresses: Vec<Multiaddr>,
pub discovery_addresses: Vec<Multiaddr>,
pub metadata: MetaData,
}
@@ -706,6 +708,15 @@ pub struct DataColumnIndicesQuery {
#[serde(transparent)]
pub struct ValidatorIndexData(#[serde(with = "serde_utils::quoted_u64_vec")] pub Vec<u64>);
impl<'de, T> ContextDeserialize<'de, T> for ValidatorIndexData {
fn context_deserialize<D>(deserializer: D, _context: T) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
Self::deserialize(deserializer)
}
}
/// Borrowed variant of `ValidatorIndexData`, for serializing/sending.
#[derive(Clone, Copy, Serialize)]
#[serde(transparent)]
@@ -1194,8 +1205,6 @@ pub enum EventKind<E: EthSpec> {
LateHead(SseLateHead),
LightClientFinalityUpdate(Box<BeaconResponse<LightClientFinalityUpdate<E>>>),
LightClientOptimisticUpdate(Box<BeaconResponse<LightClientOptimisticUpdate<E>>>),
#[cfg(feature = "lighthouse")]
BlockReward(BlockReward),
PayloadAttributes(VersionedSsePayloadAttributes),
ProposerSlashing(Box<ProposerSlashing>),
AttesterSlashing(Box<AttesterSlashing<E>>),
@@ -1220,8 +1229,6 @@ impl<E: EthSpec> EventKind<E> {
EventKind::LateHead(_) => "late_head",
EventKind::LightClientFinalityUpdate(_) => "light_client_finality_update",
EventKind::LightClientOptimisticUpdate(_) => "light_client_optimistic_update",
#[cfg(feature = "lighthouse")]
EventKind::BlockReward(_) => "block_reward",
EventKind::ProposerSlashing(_) => "proposer_slashing",
EventKind::AttesterSlashing(_) => "attester_slashing",
EventKind::BlsToExecutionChange(_) => "bls_to_execution_change",
@@ -1297,10 +1304,6 @@ impl<E: EthSpec> EventKind<E> {
})?),
)))
}
#[cfg(feature = "lighthouse")]
"block_reward" => Ok(EventKind::BlockReward(serde_json::from_str(data).map_err(
|e| ServerError::InvalidServerSentEvent(format!("Block Reward: {:?}", e)),
)?)),
"attester_slashing" => Ok(EventKind::AttesterSlashing(
serde_json::from_str(data).map_err(|e| {
ServerError::InvalidServerSentEvent(format!("Attester Slashing: {:?}", e))
@@ -1350,8 +1353,6 @@ pub enum EventTopic {
PayloadAttributes,
LightClientFinalityUpdate,
LightClientOptimisticUpdate,
#[cfg(feature = "lighthouse")]
BlockReward,
AttesterSlashing,
ProposerSlashing,
BlsToExecutionChange,
@@ -1377,8 +1378,6 @@ impl FromStr for EventTopic {
"late_head" => Ok(EventTopic::LateHead),
"light_client_finality_update" => Ok(EventTopic::LightClientFinalityUpdate),
"light_client_optimistic_update" => Ok(EventTopic::LightClientOptimisticUpdate),
#[cfg(feature = "lighthouse")]
"block_reward" => Ok(EventTopic::BlockReward),
"attester_slashing" => Ok(EventTopic::AttesterSlashing),
"proposer_slashing" => Ok(EventTopic::ProposerSlashing),
"bls_to_execution_change" => Ok(EventTopic::BlsToExecutionChange),
@@ -1405,8 +1404,6 @@ impl fmt::Display for EventTopic {
EventTopic::LateHead => write!(f, "late_head"),
EventTopic::LightClientFinalityUpdate => write!(f, "light_client_finality_update"),
EventTopic::LightClientOptimisticUpdate => write!(f, "light_client_optimistic_update"),
#[cfg(feature = "lighthouse")]
EventTopic::BlockReward => write!(f, "block_reward"),
EventTopic::AttesterSlashing => write!(f, "attester_slashing"),
EventTopic::ProposerSlashing => write!(f, "proposer_slashing"),
EventTopic::BlsToExecutionChange => write!(f, "bls_to_execution_change"),
@@ -1599,7 +1596,7 @@ pub struct BroadcastValidationQuery {
}
pub mod serde_status_code {
use crate::StatusCode;
use reqwest::StatusCode;
use serde::{Deserialize, Serialize, de::Error};
pub fn serialize<S>(status_code: &StatusCode, ser: S) -> Result<S::Ok, S::Error>
@@ -1718,7 +1715,7 @@ pub type JsonProduceBlockV3Response<E> =
pub enum FullBlockContents<E: EthSpec> {
/// This is a full deneb variant with block and blobs.
BlockContents(BlockContents<E>),
/// This variant is for all pre-deneb full blocks.
/// This variant is for all pre-deneb full blocks or post-gloas beacon block.
Block(BeaconBlock<E>),
}
@@ -1746,6 +1743,20 @@ pub struct ProduceBlockV3Metadata {
pub consensus_block_value: Uint256,
}
/// Metadata about a `produce_block_v4` response which is returned in the body & headers.
#[derive(Debug, Deserialize, Serialize)]
pub struct ProduceBlockV4Metadata {
// The consensus version is serialized & deserialized by `ForkVersionedResponse`.
#[serde(
skip_serializing,
skip_deserializing,
default = "dummy_consensus_version"
)]
pub consensus_version: ForkName,
#[serde(with = "serde_utils::u256_dec")]
pub consensus_block_value: Uint256,
}
impl<E: EthSpec> FullBlockContents<E> {
pub fn new(block: BeaconBlock<E>, blob_data: Option<(KzgProofs<E>, BlobsList<E>)>) -> Self {
match blob_data {
@@ -1907,6 +1918,27 @@ impl TryFrom<&HeaderMap> for ProduceBlockV3Metadata {
}
}
impl TryFrom<&HeaderMap> for ProduceBlockV4Metadata {
type Error = String;
fn try_from(headers: &HeaderMap) -> Result<Self, Self::Error> {
let consensus_version = parse_required_header(headers, CONSENSUS_VERSION_HEADER, |s| {
s.parse::<ForkName>()
.map_err(|e| format!("invalid {CONSENSUS_VERSION_HEADER}: {e:?}"))
})?;
let consensus_block_value =
parse_required_header(headers, CONSENSUS_BLOCK_VALUE_HEADER, |s| {
Uint256::from_str_radix(s, 10)
.map_err(|e| format!("invalid {CONSENSUS_BLOCK_VALUE_HEADER}: {e:?}"))
})?;
Ok(ProduceBlockV4Metadata {
consensus_version,
consensus_block_value,
})
}
}
/// A wrapper over a [`SignedBeaconBlock`] or a [`SignedBlockContents`].
#[derive(Clone, Debug, PartialEq, Encode, Serialize)]
#[serde(untagged)]
@@ -1954,7 +1986,7 @@ impl<E: EthSpec> PublishBlockRequest<E> {
/// SSZ decode with fork variant determined by `fork_name`.
pub fn from_ssz_bytes(bytes: &[u8], fork_name: ForkName) -> Result<Self, DecodeError> {
if fork_name.deneb_enabled() {
if fork_name.deneb_enabled() && !fork_name.gloas_enabled() {
let mut builder = ssz::SszDecoderBuilder::new(bytes);
builder.register_anonymous_variable_length_item()?;
builder.register_type::<KzgProofs<E>>()?;

View File

@@ -49,7 +49,7 @@ ELECTRA_FORK_VERSION: 0x0500006f
ELECTRA_FORK_EPOCH: 948224 # Thu Mar 6 2025 09:43:40 GMT+0000
# Fulu
FULU_FORK_VERSION: 0x0600006f
FULU_FORK_EPOCH: 18446744073709551615
FULU_FORK_EPOCH: 1353216 # Mon Mar 16 2026 09:33:00 UTC
# Gloas
GLOAS_FORK_VERSION: 0x0700006f
GLOAS_FORK_EPOCH: 18446744073709551615

View File

@@ -121,7 +121,7 @@ impl Observe for ProcessHealth {
pid_mem_shared_memory_size: process_mem.shared(),
pid_process_seconds_total: process_times.busy().as_secs()
+ process_times.children_system().as_secs()
+ process_times.children_system().as_secs(),
+ process_times.children_user().as_secs(),
})
}
}

View File

@@ -5,7 +5,8 @@ authors = ["blacktemplar <blacktemplar@a1.net>"]
edition = { workspace = true }
[features]
test_logger = [] # Print log output to stderr when running tests instead of dropping it
# Print log output to stderr when running tests instead of dropping it.
test_logger = []
[dependencies]
chrono = { version = "0.4", default-features = false, features = ["clock", "std"] }
@@ -13,7 +14,7 @@ logroller = { workspace = true }
metrics = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
tokio = { workspace = true, features = [ "time" ] }
tokio = { workspace = true, features = ["time"] }
tracing = { workspace = true }
tracing-appender = { workspace = true }
tracing-core = { workspace = true }

View File

@@ -35,7 +35,4 @@ tikv-jemallocator = { version = "0.6.0", optional = true, features = ["stats"] }
# Jemalloc's background_threads feature requires Linux (pthreads).
[target.'cfg(target_os = "linux")'.dependencies]
tikv-jemallocator = { version = "0.6.0", optional = true, features = [
"stats",
"background_threads",
] }
tikv-jemallocator = { version = "0.6.0", optional = true, features = ["stats", "background_threads"] }

View File

@@ -9,6 +9,7 @@ edition = { workspace = true }
bytes = { workspace = true }
eth2 = { workspace = true }
headers = "0.3.2"
reqwest = { workspace = true }
safe_arith = { workspace = true }
serde = { workspace = true }
serde_array_query = "0.1.0"

View File

@@ -1,4 +1,4 @@
use eth2::StatusCode;
use reqwest::StatusCode;
use warp::Rejection;
/// Convert from a "new" `http::StatusCode` to a `warp` compatible one.