Merge branch 'unstable' into vc-fallback

This commit is contained in:
Mac L
2023-11-02 16:27:14 +11:00
546 changed files with 32366 additions and 8974 deletions

View File

@@ -120,6 +120,7 @@ pub struct Timeouts {
pub get_beacon_blocks_ssz: Duration,
pub get_debug_beacon_states: Duration,
pub get_deposit_snapshot: Duration,
pub get_validator_block_ssz: Duration,
}
impl Timeouts {
@@ -135,6 +136,7 @@ impl Timeouts {
get_beacon_blocks_ssz: timeout,
get_debug_beacon_states: timeout,
get_deposit_snapshot: timeout,
get_validator_block_ssz: timeout,
}
}
}
@@ -694,8 +696,8 @@ impl BeaconNodeHttpClient {
///
/// Returns `Ok(None)` on a 404 error.
pub async fn post_beacon_blocks<T: EthSpec, Payload: AbstractExecPayload<T>>(
self,
block: &SignedBeaconBlock<T, Payload>,
&self,
block_contents: &SignedBlockContents<T, Payload>,
) -> Result<(), Error> {
let mut path = self.eth_path(V1)?;
@@ -704,7 +706,7 @@ impl BeaconNodeHttpClient {
.push("beacon")
.push("blocks");
self.post_with_timeout(path, block, self.timeouts.proposal)
self.post_with_timeout(path, block_contents, self.timeouts.proposal)
.await?;
Ok(())
@@ -715,7 +717,7 @@ impl BeaconNodeHttpClient {
/// Returns `Ok(None)` on a 404 error.
pub async fn post_beacon_blocks_ssz<T: EthSpec, Payload: AbstractExecPayload<T>>(
&self,
block: &SignedBeaconBlock<T, Payload>,
block_contents: &SignedBlockContents<T, Payload>,
) -> Result<(), Error> {
let mut path = self.eth_path(V1)?;
@@ -724,8 +726,12 @@ impl BeaconNodeHttpClient {
.push("beacon")
.push("blocks");
self.post_generic_with_ssz_body(path, block.as_ssz_bytes(), Some(self.timeouts.proposal))
.await?;
self.post_generic_with_ssz_body(
path,
block_contents.as_ssz_bytes(),
Some(self.timeouts.proposal),
)
.await?;
Ok(())
}
@@ -735,7 +741,7 @@ impl BeaconNodeHttpClient {
/// Returns `Ok(None)` on a 404 error.
pub async fn post_beacon_blinded_blocks<T: EthSpec, Payload: AbstractExecPayload<T>>(
&self,
block: &SignedBeaconBlock<T, Payload>,
block: &SignedBlockContents<T, Payload>,
) -> Result<(), Error> {
let mut path = self.eth_path(V1)?;
@@ -755,7 +761,7 @@ impl BeaconNodeHttpClient {
/// Returns `Ok(None)` on a 404 error.
pub async fn post_beacon_blinded_blocks_ssz<T: EthSpec, Payload: AbstractExecPayload<T>>(
&self,
block: &SignedBeaconBlock<T, Payload>,
block: &SignedBlockContents<T, Payload>,
) -> Result<(), Error> {
let mut path = self.eth_path(V1)?;
@@ -809,14 +815,14 @@ impl BeaconNodeHttpClient {
/// `POST v2/beacon/blocks`
pub async fn post_beacon_blocks_v2<T: EthSpec, Payload: AbstractExecPayload<T>>(
&self,
block: &SignedBeaconBlock<T, Payload>,
block_contents: &SignedBlockContents<T, Payload>,
validation_level: Option<BroadcastValidation>,
) -> Result<(), Error> {
self.post_generic_with_consensus_version(
self.post_beacon_blocks_v2_path(validation_level)?,
block,
block_contents,
Some(self.timeouts.proposal),
block.message().body().fork_name(),
block_contents.signed_block().message().body().fork_name(),
)
.await?;
@@ -826,14 +832,14 @@ impl BeaconNodeHttpClient {
/// `POST v2/beacon/blocks`
pub async fn post_beacon_blocks_v2_ssz<T: EthSpec, Payload: AbstractExecPayload<T>>(
&self,
block: &SignedBeaconBlock<T, Payload>,
block_contents: &SignedBlockContents<T, Payload>,
validation_level: Option<BroadcastValidation>,
) -> Result<(), Error> {
self.post_generic_with_consensus_version_and_ssz_body(
self.post_beacon_blocks_v2_path(validation_level)?,
block.as_ssz_bytes(),
block_contents.as_ssz_bytes(),
Some(self.timeouts.proposal),
block.message().body().fork_name(),
block_contents.signed_block().message().body().fork_name(),
)
.await?;
@@ -841,16 +847,16 @@ impl BeaconNodeHttpClient {
}
/// `POST v2/beacon/blinded_blocks`
pub async fn post_beacon_blinded_blocks_v2<T: EthSpec>(
pub async fn post_beacon_blinded_blocks_v2<T: EthSpec, Payload: AbstractExecPayload<T>>(
&self,
block: &SignedBlindedBeaconBlock<T>,
block_contents: &SignedBlockContents<T, Payload>,
validation_level: Option<BroadcastValidation>,
) -> Result<(), Error> {
self.post_generic_with_consensus_version(
self.post_beacon_blinded_blocks_v2_path(validation_level)?,
block,
block_contents,
Some(self.timeouts.proposal),
block.message().body().fork_name(),
block_contents.signed_block().message().body().fork_name(),
)
.await?;
@@ -860,14 +866,14 @@ impl BeaconNodeHttpClient {
/// `POST v2/beacon/blinded_blocks`
pub async fn post_beacon_blinded_blocks_v2_ssz<T: EthSpec>(
&self,
block: &SignedBlindedBeaconBlock<T>,
block_contents: &SignedBlindedBlockContents<T>,
validation_level: Option<BroadcastValidation>,
) -> Result<(), Error> {
self.post_generic_with_consensus_version_and_ssz_body(
self.post_beacon_blinded_blocks_v2_path(validation_level)?,
block.as_ssz_bytes(),
block_contents.as_ssz_bytes(),
Some(self.timeouts.proposal),
block.message().body().fork_name(),
block_contents.signed_block().message().body().fork_name(),
)
.await?;
@@ -885,6 +891,17 @@ impl BeaconNodeHttpClient {
Ok(path)
}
/// Path for `v1/beacon/blob_sidecars/{block_id}`
pub fn get_blobs_path(&self, block_id: BlockId) -> Result<Url, Error> {
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
.push("beacon")
.push("blob_sidecars")
.push(&block_id.to_string());
Ok(path)
}
/// Path for `v1/beacon/blinded_blocks/{block_id}`
pub fn get_beacon_blinded_blocks_path(&self, block_id: BlockId) -> Result<Url, Error> {
let mut path = self.eth_path(V1)?;
@@ -907,9 +924,23 @@ impl BeaconNodeHttpClient {
Error,
> {
let path = self.get_beacon_blocks_path(block_id)?;
let response = match self.get_response(path, |b| b).await.optional()? {
Some(res) => res,
None => return Ok(None),
let Some(response) = self.get_response(path, |b| b).await.optional()? else {
return Ok(None);
};
Ok(Some(response.json().await?))
}
/// `GET v1/beacon/blob_sidecars/{block_id}`
///
/// Returns `Ok(None)` on a 404 error.
pub async fn get_blobs<T: EthSpec>(
&self,
block_id: BlockId,
) -> Result<Option<GenericResponse<BlobSidecarList<T>>>, Error> {
let path = self.get_blobs_path(block_id)?;
let Some(response) = self.get_response(path, |b| b).await.optional()? else {
return Ok(None);
};
Ok(Some(response.json().await?))
@@ -926,9 +957,8 @@ impl BeaconNodeHttpClient {
Error,
> {
let path = self.get_beacon_blinded_blocks_path(block_id)?;
let response = match self.get_response(path, |b| b).await.optional()? {
Some(res) => res,
None => return Ok(None),
let Some(response) = self.get_response(path, |b| b).await.optional()? else {
return Ok(None);
};
Ok(Some(response.json().await?))
@@ -1269,6 +1299,23 @@ impl BeaconNodeHttpClient {
Ok(())
}
// GET builder/states/{state_id}/expected_withdrawals
pub async fn get_expected_withdrawals(
&self,
state_id: &StateId,
) -> Result<ExecutionOptimisticFinalizedResponse<Vec<Withdrawal>>, Error> {
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
.push("builder")
.push("states")
.push(&state_id.to_string())
.push("expected_withdrawals");
self.get(path).await
}
/// `POST validator/contribution_and_proofs`
pub async fn post_validator_contribution_and_proofs<T: EthSpec>(
&self,
@@ -1584,19 +1631,19 @@ impl BeaconNodeHttpClient {
slot: Slot,
randao_reveal: &SignatureBytes,
graffiti: Option<&Graffiti>,
) -> Result<ForkVersionedResponse<BeaconBlock<T, Payload>>, Error> {
) -> Result<ForkVersionedResponse<BlockContents<T, Payload>>, Error> {
self.get_validator_blocks_modular(slot, randao_reveal, graffiti, SkipRandaoVerification::No)
.await
}
/// `GET v2/validator/blocks/{slot}`
pub async fn get_validator_blocks_modular<T: EthSpec, Payload: AbstractExecPayload<T>>(
/// returns `GET v2/validator/blocks/{slot}` URL path
pub async fn get_validator_blocks_path<T: EthSpec, Payload: AbstractExecPayload<T>>(
&self,
slot: Slot,
randao_reveal: &SignatureBytes,
graffiti: Option<&Graffiti>,
skip_randao_verification: SkipRandaoVerification,
) -> Result<ForkVersionedResponse<BeaconBlock<T, Payload>>, Error> {
) -> Result<Url, Error> {
let mut path = self.eth_path(V2)?;
path.path_segments_mut()
@@ -1618,16 +1665,73 @@ impl BeaconNodeHttpClient {
.append_pair("skip_randao_verification", "");
}
Ok(path)
}
/// `GET v2/validator/blocks/{slot}`
pub async fn get_validator_blocks_modular<T: EthSpec, Payload: AbstractExecPayload<T>>(
&self,
slot: Slot,
randao_reveal: &SignatureBytes,
graffiti: Option<&Graffiti>,
skip_randao_verification: SkipRandaoVerification,
) -> Result<ForkVersionedResponse<BlockContents<T, Payload>>, Error> {
let path = self
.get_validator_blocks_path::<T, Payload>(
slot,
randao_reveal,
graffiti,
skip_randao_verification,
)
.await?;
self.get(path).await
}
/// `GET v2/validator/blocks/{slot}` in ssz format
pub async fn get_validator_blocks_ssz<T: EthSpec, Payload: AbstractExecPayload<T>>(
&self,
slot: Slot,
randao_reveal: &SignatureBytes,
graffiti: Option<&Graffiti>,
) -> Result<Option<Vec<u8>>, Error> {
self.get_validator_blocks_modular_ssz::<T, Payload>(
slot,
randao_reveal,
graffiti,
SkipRandaoVerification::No,
)
.await
}
/// `GET v2/validator/blocks/{slot}` in ssz format
pub async fn get_validator_blocks_modular_ssz<T: EthSpec, Payload: AbstractExecPayload<T>>(
&self,
slot: Slot,
randao_reveal: &SignatureBytes,
graffiti: Option<&Graffiti>,
skip_randao_verification: SkipRandaoVerification,
) -> Result<Option<Vec<u8>>, Error> {
let path = self
.get_validator_blocks_path::<T, Payload>(
slot,
randao_reveal,
graffiti,
skip_randao_verification,
)
.await?;
self.get_bytes_opt_accept_header(path, Accept::Ssz, self.timeouts.get_validator_block_ssz)
.await
}
/// `GET v2/validator/blinded_blocks/{slot}`
pub async fn get_validator_blinded_blocks<T: EthSpec, Payload: AbstractExecPayload<T>>(
&self,
slot: Slot,
randao_reveal: &SignatureBytes,
graffiti: Option<&Graffiti>,
) -> Result<ForkVersionedResponse<BeaconBlock<T, Payload>>, Error> {
) -> Result<ForkVersionedResponse<BlockContents<T, Payload>>, Error> {
self.get_validator_blinded_blocks_modular(
slot,
randao_reveal,
@@ -1637,17 +1741,14 @@ impl BeaconNodeHttpClient {
.await
}
/// `GET v1/validator/blinded_blocks/{slot}`
pub async fn get_validator_blinded_blocks_modular<
T: EthSpec,
Payload: AbstractExecPayload<T>,
>(
/// returns `GET v1/validator/blinded_blocks/{slot}` URL path
pub async fn get_validator_blinded_blocks_path<T: EthSpec, Payload: AbstractExecPayload<T>>(
&self,
slot: Slot,
randao_reveal: &SignatureBytes,
graffiti: Option<&Graffiti>,
skip_randao_verification: SkipRandaoVerification,
) -> Result<ForkVersionedResponse<BeaconBlock<T, Payload>>, Error> {
) -> Result<Url, Error> {
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
@@ -1669,9 +1770,71 @@ impl BeaconNodeHttpClient {
.append_key_only("skip_randao_verification");
}
Ok(path)
}
/// `GET v1/validator/blinded_blocks/{slot}`
pub async fn get_validator_blinded_blocks_modular<
T: EthSpec,
Payload: AbstractExecPayload<T>,
>(
&self,
slot: Slot,
randao_reveal: &SignatureBytes,
graffiti: Option<&Graffiti>,
skip_randao_verification: SkipRandaoVerification,
) -> Result<ForkVersionedResponse<BlockContents<T, Payload>>, Error> {
let path = self
.get_validator_blinded_blocks_path::<T, Payload>(
slot,
randao_reveal,
graffiti,
skip_randao_verification,
)
.await?;
self.get(path).await
}
/// `GET v2/validator/blinded_blocks/{slot}` in ssz format
pub async fn get_validator_blinded_blocks_ssz<T: EthSpec, Payload: AbstractExecPayload<T>>(
&self,
slot: Slot,
randao_reveal: &SignatureBytes,
graffiti: Option<&Graffiti>,
) -> Result<Option<Vec<u8>>, Error> {
self.get_validator_blinded_blocks_modular_ssz::<T, Payload>(
slot,
randao_reveal,
graffiti,
SkipRandaoVerification::No,
)
.await
}
pub async fn get_validator_blinded_blocks_modular_ssz<
T: EthSpec,
Payload: AbstractExecPayload<T>,
>(
&self,
slot: Slot,
randao_reveal: &SignatureBytes,
graffiti: Option<&Graffiti>,
skip_randao_verification: SkipRandaoVerification,
) -> Result<Option<Vec<u8>>, Error> {
let path = self
.get_validator_blinded_blocks_path::<T, Payload>(
slot,
randao_reveal,
graffiti,
skip_randao_verification,
)
.await?;
self.get_bytes_opt_accept_header(path, Accept::Ssz, self.timeouts.get_validator_block_ssz)
.await
}
/// `GET validator/attestation_data?slot,committee_index`
pub async fn get_validator_attestation_data(
&self,

View File

@@ -20,7 +20,7 @@ use reqwest::IntoUrl;
use serde::{Deserialize, Serialize};
use ssz::four_byte_option_impl;
use ssz_derive::{Decode, Encode};
use store::{AnchorInfo, Split, StoreConfig};
use store::{AnchorInfo, BlobInfo, Split, StoreConfig};
pub use attestation_performance::{
AttestationPerformance, AttestationPerformanceQuery, AttestationPerformanceStatistics,
@@ -95,8 +95,8 @@ pub struct ValidatorInclusionData {
#[cfg(target_os = "linux")]
use {
procinfo::pid, psutil::cpu::os::linux::CpuTimesExt,
psutil::memory::os::linux::VirtualMemoryExt, psutil::process::Process,
psutil::cpu::os::linux::CpuTimesExt, psutil::memory::os::linux::VirtualMemoryExt,
psutil::process::Process,
};
/// Reports on the health of the Lighthouse instance.
@@ -238,7 +238,7 @@ pub struct ProcessHealth {
/// The pid of this process.
pub pid: u32,
/// The number of threads used by this pid.
pub pid_num_threads: i32,
pub pid_num_threads: i64,
/// The total resident memory used by this pid.
pub pid_mem_resident_set_size: u64,
/// The total virtual memory used by this pid.
@@ -262,7 +262,12 @@ impl ProcessHealth {
.memory_info()
.map_err(|e| format!("Unable to get process memory info: {:?}", e))?;
let stat = pid::stat_self().map_err(|e| format!("Unable to get stat: {:?}", e))?;
let me = procfs::process::Process::myself()
.map_err(|e| format!("Unable to get process: {:?}", e))?;
let stat = me
.stat()
.map_err(|e| format!("Unable to get stat: {:?}", e))?;
let process_times = process
.cpu_times()
.map_err(|e| format!("Unable to get process cpu times : {:?}", e))?;
@@ -359,6 +364,7 @@ pub struct DatabaseInfo {
pub config: StoreConfig,
pub split: Split,
pub anchor: Option<AnchorInfo>,
pub blob_info: BlobInfo,
}
impl BeaconNodeHttpClient {

View File

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

View File

@@ -4,11 +4,17 @@
use crate::Error as ServerError;
use lighthouse_network::{ConnectionDirection, Enr, Multiaddr, PeerConnectionStatus};
use mediatype::{names, MediaType, MediaTypeList};
use serde::{Deserialize, Serialize};
use serde::{Deserialize, Deserializer, Serialize};
use serde_json::Value;
use ssz::{Decode, DecodeError};
use ssz_derive::{Decode, Encode};
use std::convert::TryFrom;
use std::fmt::{self, Display};
use std::str::{from_utf8, FromStr};
use std::time::Duration;
use tree_hash::TreeHash;
use types::beacon_block_body::KzgCommitments;
use types::builder_bid::BlindedBlobsBundle;
pub use types::*;
#[cfg(feature = "lighthouse")]
@@ -581,6 +587,11 @@ pub struct SyncingData {
pub sync_distance: Slot,
}
#[derive(Serialize, Deserialize)]
pub struct ExpectedWithdrawalsQuery {
pub proposal_slot: Option<Slot>,
}
#[derive(Clone, PartialEq, Debug, Deserialize)]
#[serde(try_from = "String", bound = "T: FromStr")]
pub struct QueryVec<T: FromStr> {
@@ -647,6 +658,13 @@ pub struct ValidatorBalancesQuery {
pub id: Option<Vec<ValidatorId>>,
}
#[derive(Clone, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct BlobIndicesQuery {
#[serde(default, deserialize_with = "option_query_vec")]
pub indices: Option<Vec<u64>>,
}
#[derive(Clone, Serialize, Deserialize)]
#[serde(transparent)]
pub struct ValidatorIndexData(#[serde(with = "serde_utils::quoted_u64_vec")] pub Vec<u64>);
@@ -870,6 +888,28 @@ pub struct SseBlock {
pub execution_optimistic: bool,
}
#[derive(PartialEq, Debug, Serialize, Deserialize, Clone)]
pub struct SseBlobSidecar {
pub block_root: Hash256,
#[serde(with = "serde_utils::quoted_u64")]
pub index: u64,
pub slot: Slot,
pub kzg_commitment: KzgCommitment,
pub versioned_hash: VersionedHash,
}
impl SseBlobSidecar {
pub fn from_blob_sidecar<E: EthSpec>(blob_sidecar: &BlobSidecar<E>) -> SseBlobSidecar {
SseBlobSidecar {
block_root: blob_sidecar.block_root,
index: blob_sidecar.index,
slot: blob_sidecar.slot,
kzg_commitment: blob_sidecar.kzg_commitment,
versioned_hash: blob_sidecar.kzg_commitment.calculate_versioned_hash(),
}
}
}
#[derive(PartialEq, Debug, Serialize, Deserialize, Clone)]
pub struct SseFinalizedCheckpoint {
pub block: Hash256,
@@ -918,7 +958,7 @@ pub struct SseLateHead {
}
#[superstruct(
variants(V1, V2),
variants(V1, V2, V3),
variant_attributes(derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize))
)]
#[derive(Clone, Debug, Eq, Hash, PartialEq, Deserialize, Serialize)]
@@ -931,8 +971,10 @@ pub struct SsePayloadAttributes {
pub prev_randao: Hash256,
#[superstruct(getter(copy))]
pub suggested_fee_recipient: Address,
#[superstruct(only(V2))]
#[superstruct(only(V2, V3))]
pub withdrawals: Vec<Withdrawal>,
#[superstruct(only(V3), partial_getter(copy))]
pub parent_beacon_block_root: Hash256,
}
#[derive(PartialEq, Debug, Deserialize, Serialize, Clone)]
@@ -962,6 +1004,9 @@ impl ForkVersionDeserialize for SsePayloadAttributes {
ForkName::Capella => serde_json::from_value(value)
.map(Self::V2)
.map_err(serde::de::Error::custom),
ForkName::Deneb => serde_json::from_value(value)
.map(Self::V3)
.map_err(serde::de::Error::custom),
ForkName::Base | ForkName::Altair => Err(serde::de::Error::custom(format!(
"SsePayloadAttributes deserialization for {fork_name} not implemented"
))),
@@ -995,6 +1040,7 @@ impl ForkVersionDeserialize for SseExtendedPayloadAttributes {
pub enum EventKind<T: EthSpec> {
Attestation(Box<Attestation<T>>),
Block(SseBlock),
BlobSidecar(SseBlobSidecar),
FinalizedCheckpoint(SseFinalizedCheckpoint),
Head(SseHead),
VoluntaryExit(SignedVoluntaryExit),
@@ -1011,6 +1057,7 @@ impl<T: EthSpec> EventKind<T> {
match self {
EventKind::Head(_) => "head",
EventKind::Block(_) => "block",
EventKind::BlobSidecar(_) => "blob_sidecar",
EventKind::Attestation(_) => "attestation",
EventKind::VoluntaryExit(_) => "voluntary_exit",
EventKind::FinalizedCheckpoint(_) => "finalized_checkpoint",
@@ -1048,6 +1095,9 @@ impl<T: EthSpec> EventKind<T> {
"block" => Ok(EventKind::Block(serde_json::from_str(data).map_err(
|e| ServerError::InvalidServerSentEvent(format!("Block: {:?}", e)),
)?)),
"blob_sidecar" => Ok(EventKind::BlobSidecar(serde_json::from_str(data).map_err(
|e| ServerError::InvalidServerSentEvent(format!("Blob Sidecar: {:?}", e)),
)?)),
"chain_reorg" => Ok(EventKind::ChainReorg(serde_json::from_str(data).map_err(
|e| ServerError::InvalidServerSentEvent(format!("Chain Reorg: {:?}", e)),
)?)),
@@ -1100,6 +1150,7 @@ pub struct EventQuery {
pub enum EventTopic {
Head,
Block,
BlobSidecar,
Attestation,
VoluntaryExit,
FinalizedCheckpoint,
@@ -1118,6 +1169,7 @@ impl FromStr for EventTopic {
match s {
"head" => Ok(EventTopic::Head),
"block" => Ok(EventTopic::Block),
"blob_sidecar" => Ok(EventTopic::BlobSidecar),
"attestation" => Ok(EventTopic::Attestation),
"voluntary_exit" => Ok(EventTopic::VoluntaryExit),
"finalized_checkpoint" => Ok(EventTopic::FinalizedCheckpoint),
@@ -1137,6 +1189,7 @@ impl fmt::Display for EventTopic {
match self {
EventTopic::Head => write!(f, "head"),
EventTopic::Block => write!(f, "block"),
EventTopic::BlobSidecar => write!(f, "blob_sidecar"),
EventTopic::Attestation => write!(f, "attestation"),
EventTopic::VoluntaryExit => write!(f, "voluntary_exit"),
EventTopic::FinalizedCheckpoint => write!(f, "finalized_checkpoint"),
@@ -1311,9 +1364,31 @@ pub struct BroadcastValidationQuery {
pub broadcast_validation: BroadcastValidation,
}
pub mod serde_status_code {
use crate::StatusCode;
use serde::{de::Error, Deserialize, Serialize};
pub fn serialize<S>(status_code: &StatusCode, ser: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
status_code.as_u16().serialize(ser)
}
pub fn deserialize<'de, D>(de: D) -> Result<StatusCode, D::Error>
where
D: serde::de::Deserializer<'de>,
{
let status_code = u16::deserialize(de)?;
StatusCode::try_from(status_code).map_err(D::Error::custom)
}
}
#[cfg(test)]
mod tests {
use super::*;
use ssz::Encode;
use std::sync::Arc;
#[test]
fn query_vec() {
@@ -1348,4 +1423,615 @@ mod tests {
Accept::Any
);
}
#[test]
fn ssz_signed_block_contents_pre_deneb() {
type E = MainnetEthSpec;
let spec = ForkName::Capella.make_genesis_spec(E::default_spec());
let block: SignedBlockContents<E, FullPayload<E>> = SignedBeaconBlock::from_block(
BeaconBlock::<E>::Capella(BeaconBlockCapella::empty(&spec)),
Signature::empty(),
)
.try_into()
.expect("should convert into signed block contents");
let decoded: SignedBlockContents<E> =
SignedBlockContents::from_ssz_bytes(&block.as_ssz_bytes(), &spec)
.expect("should decode Block");
assert!(matches!(decoded, SignedBlockContents::Block(_)));
}
#[test]
fn ssz_signed_block_contents_with_blobs() {
type E = MainnetEthSpec;
let spec = ForkName::Deneb.make_genesis_spec(E::default_spec());
let block = SignedBeaconBlock::from_block(
BeaconBlock::<E>::Deneb(BeaconBlockDeneb::empty(&spec)),
Signature::empty(),
);
let blobs = SignedSidecarList::from(vec![SignedSidecar {
message: Arc::new(BlobSidecar::empty()),
signature: Signature::empty(),
_phantom: Default::default(),
}]);
let signed_block_contents = SignedBlockContents::new(block, Some(blobs));
let decoded: SignedBlockContents<E, FullPayload<E>> =
SignedBlockContents::from_ssz_bytes(&signed_block_contents.as_ssz_bytes(), &spec)
.expect("should decode BlockAndBlobSidecars");
assert!(matches!(
decoded,
SignedBlockContents::BlockAndBlobSidecars(_)
));
}
#[test]
fn ssz_signed_blinded_block_contents_with_blobs() {
type E = MainnetEthSpec;
let mut spec = E::default_spec();
spec.altair_fork_epoch = Some(Epoch::new(0));
spec.bellatrix_fork_epoch = Some(Epoch::new(0));
spec.capella_fork_epoch = Some(Epoch::new(0));
spec.deneb_fork_epoch = Some(Epoch::new(0));
let blinded_block = SignedBeaconBlock::from_block(
BeaconBlock::<E, BlindedPayload<E>>::Deneb(BeaconBlockDeneb::empty(&spec)),
Signature::empty(),
);
let blinded_blobs = SignedSidecarList::from(vec![SignedSidecar {
message: Arc::new(BlindedBlobSidecar::empty()),
signature: Signature::empty(),
_phantom: Default::default(),
}]);
let signed_block_contents = SignedBlockContents::new(blinded_block, Some(blinded_blobs));
let decoded: SignedBlockContents<E, BlindedPayload<E>> =
SignedBlockContents::from_ssz_bytes(&signed_block_contents.as_ssz_bytes(), &spec)
.expect("should decode BlindedBlockAndBlobSidecars");
assert!(matches!(
decoded,
SignedBlockContents::BlindedBlockAndBlobSidecars(_)
));
}
}
/// A wrapper over a [`BeaconBlock`] or a [`BeaconBlockAndBlobSidecars`].
#[derive(Debug, Encode, Serialize, Deserialize)]
#[serde(untagged)]
#[serde(bound = "T: EthSpec")]
#[ssz(enum_behaviour = "transparent")]
pub enum BlockContents<T: EthSpec, Payload: AbstractExecPayload<T>> {
BlockAndBlobSidecars(BeaconBlockAndBlobSidecars<T, Payload>),
BlindedBlockAndBlobSidecars(BlindedBeaconBlockAndBlobSidecars<T, Payload>),
Block(BeaconBlock<T, Payload>),
}
pub type BlockContentsTuple<T, Payload> = (
BeaconBlock<T, Payload>,
Option<SidecarList<T, <Payload as AbstractExecPayload<T>>::Sidecar>>,
);
impl<T: EthSpec, Payload: AbstractExecPayload<T>> BlockContents<T, Payload> {
pub fn new(
block: BeaconBlock<T, Payload>,
blobs: Option<SidecarList<T, Payload::Sidecar>>,
) -> Self {
match (Payload::block_type(), blobs) {
(BlockType::Full, Some(blobs)) => {
Self::BlockAndBlobSidecars(BeaconBlockAndBlobSidecars {
block,
blob_sidecars: blobs,
})
}
(BlockType::Blinded, Some(blobs)) => {
Self::BlindedBlockAndBlobSidecars(BlindedBeaconBlockAndBlobSidecars {
blinded_block: block,
blinded_blob_sidecars: blobs,
})
}
(_, None) => Self::Block(block),
}
}
/// SSZ decode with fork variant determined by slot.
pub fn from_ssz_bytes(bytes: &[u8], spec: &ChainSpec) -> Result<Self, ssz::DecodeError> {
let slot_len = <Slot as Decode>::ssz_fixed_len();
let slot_bytes = bytes
.get(0..slot_len)
.ok_or(DecodeError::InvalidByteLength {
len: bytes.len(),
expected: slot_len,
})?;
let slot = Slot::from_ssz_bytes(slot_bytes)?;
let fork_at_slot = spec.fork_name_at_slot::<T>(slot);
match fork_at_slot {
ForkName::Base | ForkName::Altair | ForkName::Merge | ForkName::Capella => {
BeaconBlock::from_ssz_bytes(bytes, spec).map(|block| BlockContents::Block(block))
}
ForkName::Deneb => {
let mut builder = ssz::SszDecoderBuilder::new(bytes);
builder.register_anonymous_variable_length_item()?;
builder.register_type::<SidecarList<T, Payload::Sidecar>>()?;
let mut decoder = builder.build()?;
let block =
decoder.decode_next_with(|bytes| BeaconBlock::from_ssz_bytes(bytes, spec))?;
let blobs = decoder.decode_next()?;
Ok(BlockContents::new(block, Some(blobs)))
}
}
}
pub fn block(&self) -> &BeaconBlock<T, Payload> {
match self {
BlockContents::BlockAndBlobSidecars(block_and_sidecars) => &block_and_sidecars.block,
BlockContents::BlindedBlockAndBlobSidecars(block_and_sidecars) => {
&block_and_sidecars.blinded_block
}
BlockContents::Block(block) => block,
}
}
pub fn deconstruct(self) -> BlockContentsTuple<T, Payload> {
match self {
BlockContents::BlockAndBlobSidecars(block_and_sidecars) => (
block_and_sidecars.block,
Some(block_and_sidecars.blob_sidecars),
),
BlockContents::BlindedBlockAndBlobSidecars(block_and_sidecars) => (
block_and_sidecars.blinded_block,
Some(block_and_sidecars.blinded_blob_sidecars),
),
BlockContents::Block(block) => (block, None),
}
}
/// Signs `self`, producing a `SignedBlockContents`.
pub fn sign(
self,
secret_key: &SecretKey,
fork: &Fork,
genesis_validators_root: Hash256,
spec: &ChainSpec,
) -> SignedBlockContents<T, Payload> {
let (block, maybe_blobs) = self.deconstruct();
let signed_block = block.sign(secret_key, fork, genesis_validators_root, spec);
let signed_blobs = maybe_blobs.map(|blobs| {
blobs
.into_iter()
.map(|blob| blob.sign(secret_key, fork, genesis_validators_root, spec))
.collect::<Vec<_>>()
.into()
});
SignedBlockContents::new(signed_block, signed_blobs)
}
}
impl<T: EthSpec, Payload: AbstractExecPayload<T>> ForkVersionDeserialize
for BlockContents<T, Payload>
{
fn deserialize_by_fork<'de, D: serde::Deserializer<'de>>(
value: serde_json::value::Value,
fork_name: ForkName,
) -> Result<Self, D::Error> {
match fork_name {
ForkName::Base | ForkName::Altair | ForkName::Merge | ForkName::Capella => {
Ok(BlockContents::Block(BeaconBlock::deserialize_by_fork::<
'de,
D,
>(value, fork_name)?))
}
ForkName::Deneb => {
let block_contents = match Payload::block_type() {
BlockType::Blinded => BlockContents::BlindedBlockAndBlobSidecars(
BlindedBeaconBlockAndBlobSidecars::deserialize_by_fork::<'de, D>(
value, fork_name,
)?,
),
BlockType::Full => BlockContents::BlockAndBlobSidecars(
BeaconBlockAndBlobSidecars::deserialize_by_fork::<'de, D>(
value, fork_name,
)?,
),
};
Ok(block_contents)
}
}
}
}
impl<T: EthSpec, Payload: AbstractExecPayload<T>> Into<BeaconBlock<T, Payload>>
for BlockContents<T, Payload>
{
fn into(self) -> BeaconBlock<T, Payload> {
match self {
Self::BlockAndBlobSidecars(block_and_sidecars) => block_and_sidecars.block,
Self::BlindedBlockAndBlobSidecars(block_and_sidecars) => {
block_and_sidecars.blinded_block
}
Self::Block(block) => block,
}
}
}
pub type SignedBlockContentsTuple<T, Payload> = (
SignedBeaconBlock<T, Payload>,
Option<SignedSidecarList<T, <Payload as AbstractExecPayload<T>>::Sidecar>>,
);
pub type SignedBlindedBlockContents<E> = SignedBlockContents<E, BlindedPayload<E>>;
/// A wrapper over a [`SignedBeaconBlock`] or a [`SignedBeaconBlockAndBlobSidecars`].
#[derive(Clone, Debug, Encode, Serialize, Deserialize)]
#[serde(untagged)]
#[serde(bound = "T: EthSpec")]
#[ssz(enum_behaviour = "transparent")]
pub enum SignedBlockContents<T: EthSpec, Payload: AbstractExecPayload<T> = FullPayload<T>> {
BlockAndBlobSidecars(SignedBeaconBlockAndBlobSidecars<T, Payload>),
BlindedBlockAndBlobSidecars(SignedBlindedBeaconBlockAndBlobSidecars<T, Payload>),
Block(SignedBeaconBlock<T, Payload>),
}
impl<T: EthSpec, Payload: AbstractExecPayload<T>> SignedBlockContents<T, Payload> {
pub fn new(
block: SignedBeaconBlock<T, Payload>,
blobs: Option<SignedSidecarList<T, Payload::Sidecar>>,
) -> Self {
match (Payload::block_type(), blobs) {
(BlockType::Full, Some(blobs)) => {
Self::BlockAndBlobSidecars(SignedBeaconBlockAndBlobSidecars {
signed_block: block,
signed_blob_sidecars: blobs,
})
}
(BlockType::Blinded, Some(blobs)) => {
Self::BlindedBlockAndBlobSidecars(SignedBlindedBeaconBlockAndBlobSidecars {
signed_blinded_block: block,
signed_blinded_blob_sidecars: blobs,
})
}
(_, None) => Self::Block(block),
}
}
/// SSZ decode with fork variant determined by slot.
pub fn from_ssz_bytes(bytes: &[u8], spec: &ChainSpec) -> Result<Self, ssz::DecodeError> {
let slot_len = <Slot as Decode>::ssz_fixed_len();
let slot_bytes = bytes
.get(0..slot_len)
.ok_or(DecodeError::InvalidByteLength {
len: bytes.len(),
expected: slot_len,
})?;
let slot = Slot::from_ssz_bytes(slot_bytes)?;
let fork_at_slot = spec.fork_name_at_slot::<T>(slot);
match fork_at_slot {
ForkName::Base | ForkName::Altair | ForkName::Merge | ForkName::Capella => {
SignedBeaconBlock::from_ssz_bytes(bytes, spec)
.map(|block| SignedBlockContents::Block(block))
}
ForkName::Deneb => {
let mut builder = ssz::SszDecoderBuilder::new(bytes);
builder.register_anonymous_variable_length_item()?;
builder.register_type::<SignedSidecarList<T, Payload::Sidecar>>()?;
let mut decoder = builder.build()?;
let block = decoder
.decode_next_with(|bytes| SignedBeaconBlock::from_ssz_bytes(bytes, spec))?;
let blobs = decoder.decode_next()?;
Ok(SignedBlockContents::new(block, Some(blobs)))
}
}
}
pub fn signed_block(&self) -> &SignedBeaconBlock<T, Payload> {
match self {
SignedBlockContents::BlockAndBlobSidecars(block_and_sidecars) => {
&block_and_sidecars.signed_block
}
SignedBlockContents::BlindedBlockAndBlobSidecars(block_and_sidecars) => {
&block_and_sidecars.signed_blinded_block
}
SignedBlockContents::Block(block) => block,
}
}
pub fn blobs_cloned(&self) -> Option<SignedSidecarList<T, Payload::Sidecar>> {
match self {
SignedBlockContents::BlockAndBlobSidecars(block_and_sidecars) => {
Some(block_and_sidecars.signed_blob_sidecars.clone())
}
SignedBlockContents::BlindedBlockAndBlobSidecars(block_and_sidecars) => {
Some(block_and_sidecars.signed_blinded_blob_sidecars.clone())
}
SignedBlockContents::Block(_block) => None,
}
}
pub fn deconstruct(self) -> SignedBlockContentsTuple<T, Payload> {
match self {
SignedBlockContents::BlockAndBlobSidecars(block_and_sidecars) => (
block_and_sidecars.signed_block,
Some(block_and_sidecars.signed_blob_sidecars),
),
SignedBlockContents::BlindedBlockAndBlobSidecars(block_and_sidecars) => (
block_and_sidecars.signed_blinded_block,
Some(block_and_sidecars.signed_blinded_blob_sidecars),
),
SignedBlockContents::Block(block) => (block, None),
}
}
}
impl<T: EthSpec> SignedBlockContents<T, BlindedPayload<T>> {
pub fn try_into_full_block_and_blobs(
self,
maybe_full_payload_contents: Option<FullPayloadContents<T>>,
) -> Result<SignedBlockContents<T, FullPayload<T>>, String> {
match self {
SignedBlockContents::BlindedBlockAndBlobSidecars(blinded_block_and_blob_sidecars) => {
match maybe_full_payload_contents {
None | Some(FullPayloadContents::Payload(_)) => {
Err("Can't build full block contents without payload and blobs".to_string())
}
Some(FullPayloadContents::PayloadAndBlobs(payload_and_blobs)) => {
let signed_block = blinded_block_and_blob_sidecars
.signed_blinded_block
.try_into_full_block(Some(payload_and_blobs.execution_payload))
.ok_or("Failed to build full block with payload".to_string())?;
let signed_blob_sidecars: SignedBlobSidecarList<T> =
blinded_block_and_blob_sidecars
.signed_blinded_blob_sidecars
.into_iter()
.zip(payload_and_blobs.blobs_bundle.blobs)
.map(|(blinded_blob_sidecar, blob)| {
blinded_blob_sidecar.into_full_blob_sidecars(blob)
})
.collect::<Vec<_>>()
.into();
Ok(SignedBlockContents::new(
signed_block,
Some(signed_blob_sidecars),
))
}
}
}
SignedBlockContents::Block(blinded_block) => {
let full_payload_opt = maybe_full_payload_contents.map(|o| o.deconstruct().0);
blinded_block
.try_into_full_block(full_payload_opt)
.map(SignedBlockContents::Block)
.ok_or("Can't build full block without payload".to_string())
}
SignedBlockContents::BlockAndBlobSidecars(_) => Err(
"BlockAndBlobSidecars variant not expected when constructing full block"
.to_string(),
),
}
}
}
impl<T: EthSpec> SignedBlockContents<T> {
pub fn clone_as_blinded(&self) -> SignedBlindedBlockContents<T> {
let blinded_blobs = self.blobs_cloned().map(|blob_sidecars| {
blob_sidecars
.into_iter()
.map(|blob| blob.into())
.collect::<Vec<_>>()
.into()
});
SignedBlockContents::new(self.signed_block().clone_as_blinded(), blinded_blobs)
}
}
impl<T: EthSpec, Payload: AbstractExecPayload<T>> TryFrom<SignedBeaconBlock<T, Payload>>
for SignedBlockContents<T, Payload>
{
type Error = &'static str;
fn try_from(block: SignedBeaconBlock<T, Payload>) -> Result<Self, Self::Error> {
match block {
SignedBeaconBlock::Base(_)
| SignedBeaconBlock::Altair(_)
| SignedBeaconBlock::Merge(_)
| SignedBeaconBlock::Capella(_) => Ok(SignedBlockContents::Block(block)),
SignedBeaconBlock::Deneb(_) => {
Err("deneb block contents cannot be fully constructed from just the signed block")
}
}
}
}
impl<T: EthSpec, Payload: AbstractExecPayload<T>> From<SignedBlockContentsTuple<T, Payload>>
for SignedBlockContents<T, Payload>
{
fn from(block_contents_tuple: SignedBlockContentsTuple<T, Payload>) -> Self {
SignedBlockContents::new(block_contents_tuple.0, block_contents_tuple.1)
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Encode)]
#[serde(bound = "T: EthSpec")]
pub struct SignedBeaconBlockAndBlobSidecars<T: EthSpec, Payload: AbstractExecPayload<T>> {
pub signed_block: SignedBeaconBlock<T, Payload>,
pub signed_blob_sidecars: SignedSidecarList<T, Payload::Sidecar>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Encode)]
#[serde(bound = "T: EthSpec, Payload: AbstractExecPayload<T>")]
pub struct BeaconBlockAndBlobSidecars<T: EthSpec, Payload: AbstractExecPayload<T>> {
pub block: BeaconBlock<T, Payload>,
pub blob_sidecars: SidecarList<T, Payload::Sidecar>,
}
impl<T: EthSpec, Payload: AbstractExecPayload<T>> ForkVersionDeserialize
for BeaconBlockAndBlobSidecars<T, Payload>
{
fn deserialize_by_fork<'de, D: serde::Deserializer<'de>>(
value: serde_json::value::Value,
fork_name: ForkName,
) -> Result<Self, D::Error> {
#[derive(Deserialize)]
#[serde(bound = "T: EthSpec, S: Sidecar<T>")]
struct Helper<T: EthSpec, S: Sidecar<T>> {
block: serde_json::Value,
blob_sidecars: SidecarList<T, S>,
}
let helper: Helper<T, Payload::Sidecar> =
serde_json::from_value(value).map_err(serde::de::Error::custom)?;
Ok(Self {
block: BeaconBlock::deserialize_by_fork::<'de, D>(helper.block, fork_name)?,
blob_sidecars: helper.blob_sidecars,
})
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Encode)]
#[serde(bound = "T: EthSpec")]
pub struct SignedBlindedBeaconBlockAndBlobSidecars<
T: EthSpec,
Payload: AbstractExecPayload<T> = BlindedPayload<T>,
> {
pub signed_blinded_block: SignedBeaconBlock<T, Payload>,
pub signed_blinded_blob_sidecars: SignedSidecarList<T, Payload::Sidecar>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Encode)]
#[serde(bound = "T: EthSpec, Payload: AbstractExecPayload<T>")]
pub struct BlindedBeaconBlockAndBlobSidecars<
T: EthSpec,
Payload: AbstractExecPayload<T> = BlindedPayload<T>,
> {
pub blinded_block: BeaconBlock<T, Payload>,
pub blinded_blob_sidecars: SidecarList<T, Payload::Sidecar>,
}
impl<T: EthSpec, Payload: AbstractExecPayload<T>> ForkVersionDeserialize
for BlindedBeaconBlockAndBlobSidecars<T, Payload>
{
fn deserialize_by_fork<'de, D: serde::Deserializer<'de>>(
value: serde_json::value::Value,
fork_name: ForkName,
) -> Result<Self, D::Error> {
#[derive(Deserialize)]
#[serde(bound = "T: EthSpec, S: Sidecar<T>")]
struct Helper<T: EthSpec, S: Sidecar<T>> {
blinded_block: serde_json::Value,
blinded_blob_sidecars: SidecarList<T, S>,
}
let helper: Helper<T, Payload::Sidecar> =
serde_json::from_value(value).map_err(serde::de::Error::custom)?;
Ok(Self {
blinded_block: BeaconBlock::deserialize_by_fork::<'de, D>(
helper.blinded_block,
fork_name,
)?,
blinded_blob_sidecars: helper.blinded_blob_sidecars,
})
}
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Encode)]
#[serde(untagged)]
#[serde(bound = "E: EthSpec")]
#[ssz(enum_behaviour = "transparent")]
pub enum FullPayloadContents<E: EthSpec> {
Payload(ExecutionPayload<E>),
PayloadAndBlobs(ExecutionPayloadAndBlobs<E>),
}
impl<E: EthSpec> FullPayloadContents<E> {
pub fn new(
execution_payload: ExecutionPayload<E>,
maybe_blobs: Option<BlobsBundle<E>>,
) -> Self {
match maybe_blobs {
None => Self::Payload(execution_payload),
Some(blobs_bundle) => Self::PayloadAndBlobs(ExecutionPayloadAndBlobs {
execution_payload,
blobs_bundle,
}),
}
}
pub fn payload_ref(&self) -> &ExecutionPayload<E> {
match self {
FullPayloadContents::Payload(payload) => payload,
FullPayloadContents::PayloadAndBlobs(payload_and_blobs) => {
&payload_and_blobs.execution_payload
}
}
}
pub fn block_hash(&self) -> ExecutionBlockHash {
self.payload_ref().block_hash()
}
pub fn deconstruct(self) -> (ExecutionPayload<E>, Option<BlobsBundle<E>>) {
match self {
FullPayloadContents::Payload(payload) => (payload, None),
FullPayloadContents::PayloadAndBlobs(payload_and_blobs) => (
payload_and_blobs.execution_payload,
Some(payload_and_blobs.blobs_bundle),
),
}
}
}
impl<E: EthSpec> ForkVersionDeserialize for FullPayloadContents<E> {
fn deserialize_by_fork<'de, D: Deserializer<'de>>(
value: Value,
fork_name: ForkName,
) -> Result<Self, D::Error> {
match fork_name {
ForkName::Merge | ForkName::Capella => serde_json::from_value(value)
.map(Self::Payload)
.map_err(serde::de::Error::custom),
ForkName::Deneb => serde_json::from_value(value)
.map(Self::PayloadAndBlobs)
.map_err(serde::de::Error::custom),
ForkName::Base | ForkName::Altair => Err(serde::de::Error::custom(format!(
"FullPayloadContents deserialization for {fork_name} not implemented"
))),
}
}
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Encode)]
#[serde(bound = "E: EthSpec")]
pub struct ExecutionPayloadAndBlobs<E: EthSpec> {
pub execution_payload: ExecutionPayload<E>,
pub blobs_bundle: BlobsBundle<E>,
}
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize, Encode, Decode)]
#[serde(bound = "E: EthSpec")]
pub struct BlobsBundle<E: EthSpec> {
pub commitments: KzgCommitments<E>,
pub proofs: KzgProofs<E>,
#[serde(with = "ssz_types::serde_utils::list_of_hex_fixed_vec")]
pub blobs: BlobsList<E>,
}
impl<E: EthSpec> Into<BlindedBlobsBundle<E>> for BlobsBundle<E> {
fn into(self) -> BlindedBlobsBundle<E> {
BlindedBlobsBundle {
commitments: self.commitments,
proofs: self.proofs,
blob_roots: self
.blobs
.into_iter()
.map(|blob| blob.tree_hash_root())
.collect::<Vec<_>>()
.into(),
}
}
}