Merge branch 'unstable' into vc-fallback

This commit is contained in:
Mac L
2023-12-13 12:24:11 +11:00
129 changed files with 5284 additions and 3944 deletions

View File

@@ -322,6 +322,18 @@ impl BeaconNodeHttpClient {
.map_err(Into::into)
}
async fn post_with_opt_response<T: Serialize, U: IntoUrl, R: DeserializeOwned>(
&self,
url: U,
body: &T,
) -> Result<Option<R>, Error> {
if let Some(response) = self.post_generic(url, body, None).await.optional()? {
response.json().await.map_err(Into::into)
} else {
Ok(None)
}
}
/// Perform a HTTP POST request with a custom timeout.
async fn post_with_timeout<T: Serialize, U: IntoUrl>(
&self,
@@ -529,6 +541,29 @@ impl BeaconNodeHttpClient {
self.get_opt(path).await
}
/// `POST beacon/states/{state_id}/validator_balances`
///
/// Returns `Ok(None)` on a 404 error.
pub async fn post_beacon_states_validator_balances(
&self,
state_id: StateId,
ids: Vec<ValidatorId>,
) -> Result<Option<ExecutionOptimisticFinalizedResponse<Vec<ValidatorBalanceData>>>, 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("validator_balances");
let request = ValidatorBalancesRequestBody { ids };
self.post_with_opt_response(path, &request).await
}
/// `GET beacon/states/{state_id}/validators?id,status`
///
/// Returns `Ok(None)` on a 404 error.
@@ -568,6 +603,29 @@ impl BeaconNodeHttpClient {
self.get_opt(path).await
}
/// `POST beacon/states/{state_id}/validators`
///
/// Returns `Ok(None)` on a 404 error.
pub async fn post_beacon_states_validators(
&self,
state_id: StateId,
ids: Option<Vec<ValidatorId>>,
statuses: Option<Vec<ValidatorStatus>>,
) -> Result<Option<ExecutionOptimisticFinalizedResponse<Vec<ValidatorData>>>, 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("validators");
let request = ValidatorsRequestBody { ids, statuses };
self.post_with_opt_response(path, &request).await
}
/// `GET beacon/states/{state_id}/committees?slot,index,epoch`
///
/// Returns `Ok(None)` on a 404 error.
@@ -774,9 +832,9 @@ impl BeaconNodeHttpClient {
/// `POST beacon/blocks`
///
/// Returns `Ok(None)` on a 404 error.
pub async fn post_beacon_blocks<T: EthSpec, Payload: AbstractExecPayload<T>>(
pub async fn post_beacon_blocks<T: EthSpec>(
&self,
block_contents: &SignedBlockContents<T, Payload>,
block_contents: &PublishBlockRequest<T>,
) -> Result<(), Error> {
let mut path = self.eth_path(V1)?;
@@ -794,9 +852,9 @@ impl BeaconNodeHttpClient {
/// `POST beacon/blocks`
///
/// Returns `Ok(None)` on a 404 error.
pub async fn post_beacon_blocks_ssz<T: EthSpec, Payload: AbstractExecPayload<T>>(
pub async fn post_beacon_blocks_ssz<T: EthSpec>(
&self,
block_contents: &SignedBlockContents<T, Payload>,
block_contents: &PublishBlockRequest<T>,
) -> Result<(), Error> {
let mut path = self.eth_path(V1)?;
@@ -818,9 +876,9 @@ impl BeaconNodeHttpClient {
/// `POST beacon/blinded_blocks`
///
/// Returns `Ok(None)` on a 404 error.
pub async fn post_beacon_blinded_blocks<T: EthSpec, Payload: AbstractExecPayload<T>>(
pub async fn post_beacon_blinded_blocks<T: EthSpec>(
&self,
block: &SignedBlockContents<T, Payload>,
block: &SignedBlindedBeaconBlock<T>,
) -> Result<(), Error> {
let mut path = self.eth_path(V1)?;
@@ -838,9 +896,9 @@ impl BeaconNodeHttpClient {
/// `POST beacon/blinded_blocks`
///
/// Returns `Ok(None)` on a 404 error.
pub async fn post_beacon_blinded_blocks_ssz<T: EthSpec, Payload: AbstractExecPayload<T>>(
pub async fn post_beacon_blinded_blocks_ssz<T: EthSpec>(
&self,
block: &SignedBlockContents<T, Payload>,
block: &SignedBlindedBeaconBlock<T>,
) -> Result<(), Error> {
let mut path = self.eth_path(V1)?;
@@ -892,9 +950,9 @@ impl BeaconNodeHttpClient {
}
/// `POST v2/beacon/blocks`
pub async fn post_beacon_blocks_v2<T: EthSpec, Payload: AbstractExecPayload<T>>(
pub async fn post_beacon_blocks_v2<T: EthSpec>(
&self,
block_contents: &SignedBlockContents<T, Payload>,
block_contents: &PublishBlockRequest<T>,
validation_level: Option<BroadcastValidation>,
) -> Result<(), Error> {
self.post_generic_with_consensus_version(
@@ -909,9 +967,9 @@ impl BeaconNodeHttpClient {
}
/// `POST v2/beacon/blocks`
pub async fn post_beacon_blocks_v2_ssz<T: EthSpec, Payload: AbstractExecPayload<T>>(
pub async fn post_beacon_blocks_v2_ssz<T: EthSpec>(
&self,
block_contents: &SignedBlockContents<T, Payload>,
block_contents: &PublishBlockRequest<T>,
validation_level: Option<BroadcastValidation>,
) -> Result<(), Error> {
self.post_generic_with_consensus_version_and_ssz_body(
@@ -926,16 +984,16 @@ impl BeaconNodeHttpClient {
}
/// `POST v2/beacon/blinded_blocks`
pub async fn post_beacon_blinded_blocks_v2<T: EthSpec, Payload: AbstractExecPayload<T>>(
pub async fn post_beacon_blinded_blocks_v2<T: EthSpec>(
&self,
block_contents: &SignedBlockContents<T, Payload>,
signed_block: &SignedBlindedBeaconBlock<T>,
validation_level: Option<BroadcastValidation>,
) -> Result<(), Error> {
self.post_generic_with_consensus_version(
self.post_beacon_blinded_blocks_v2_path(validation_level)?,
block_contents,
signed_block,
Some(self.timeouts.proposal),
block_contents.signed_block().message().body().fork_name(),
signed_block.message().body().fork_name(),
)
.await?;
@@ -945,14 +1003,14 @@ impl BeaconNodeHttpClient {
/// `POST v2/beacon/blinded_blocks`
pub async fn post_beacon_blinded_blocks_v2_ssz<T: EthSpec>(
&self,
block_contents: &SignedBlindedBlockContents<T>,
signed_block: &SignedBlindedBeaconBlock<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_contents.as_ssz_bytes(),
signed_block.as_ssz_bytes(),
Some(self.timeouts.proposal),
block_contents.signed_block().message().body().fork_name(),
signed_block.message().body().fork_name(),
)
.await?;
@@ -1705,38 +1763,33 @@ impl BeaconNodeHttpClient {
}
/// `GET v2/validator/blocks/{slot}`
pub async fn get_validator_blocks<T: EthSpec, Payload: AbstractExecPayload<T>>(
pub async fn get_validator_blocks<T: EthSpec>(
&self,
slot: Slot,
randao_reveal: &SignatureBytes,
graffiti: Option<&Graffiti>,
) -> Result<ForkVersionedResponse<BlockContents<T, Payload>>, Error> {
) -> Result<ForkVersionedResponse<FullBlockContents<T>>, 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>>(
pub async fn get_validator_blocks_modular<T: EthSpec>(
&self,
slot: Slot,
randao_reveal: &SignatureBytes,
graffiti: Option<&Graffiti>,
skip_randao_verification: SkipRandaoVerification,
) -> Result<ForkVersionedResponse<BlockContents<T, Payload>>, Error> {
) -> Result<ForkVersionedResponse<FullBlockContents<T>>, Error> {
let path = self
.get_validator_blocks_path::<T, Payload>(
slot,
randao_reveal,
graffiti,
skip_randao_verification,
)
.get_validator_blocks_path::<T>(slot, randao_reveal, graffiti, skip_randao_verification)
.await?;
self.get(path).await
}
/// returns `GET v2/validator/blocks/{slot}` URL path
pub async fn get_validator_blocks_path<T: EthSpec, Payload: AbstractExecPayload<T>>(
pub async fn get_validator_blocks_path<T: EthSpec>(
&self,
slot: Slot,
randao_reveal: &SignatureBytes,
@@ -1842,12 +1895,12 @@ impl BeaconNodeHttpClient {
if is_blinded_payload {
let blinded_payload = response
.json::<ForkVersionedResponse<BlockContents<T, BlindedPayload<T>>>>()
.json::<ForkVersionedResponse<BlindedBeaconBlock<T>>>()
.await?;
Ok(ForkVersionedBeaconBlockType::Blinded(blinded_payload))
} else {
let full_payload = response
.json::<ForkVersionedResponse<BlockContents<T, FullPayload<T>>>>()
.json::<ForkVersionedResponse<FullBlockContents<T>>>()
.await?;
Ok(ForkVersionedBeaconBlockType::Full(full_payload))
}
@@ -1906,13 +1959,13 @@ impl BeaconNodeHttpClient {
}
/// `GET v2/validator/blocks/{slot}` in ssz format
pub async fn get_validator_blocks_ssz<T: EthSpec, Payload: AbstractExecPayload<T>>(
pub async fn get_validator_blocks_ssz<T: EthSpec>(
&self,
slot: Slot,
randao_reveal: &SignatureBytes,
graffiti: Option<&Graffiti>,
) -> Result<Option<Vec<u8>>, Error> {
self.get_validator_blocks_modular_ssz::<T, Payload>(
self.get_validator_blocks_modular_ssz::<T>(
slot,
randao_reveal,
graffiti,
@@ -1922,7 +1975,7 @@ impl BeaconNodeHttpClient {
}
/// `GET v2/validator/blocks/{slot}` in ssz format
pub async fn get_validator_blocks_modular_ssz<T: EthSpec, Payload: AbstractExecPayload<T>>(
pub async fn get_validator_blocks_modular_ssz<T: EthSpec>(
&self,
slot: Slot,
randao_reveal: &SignatureBytes,
@@ -1930,12 +1983,7 @@ impl BeaconNodeHttpClient {
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,
)
.get_validator_blocks_path::<T>(slot, randao_reveal, graffiti, skip_randao_verification)
.await?;
self.get_bytes_opt_accept_header(path, Accept::Ssz, self.timeouts.get_validator_block_ssz)
@@ -1943,12 +1991,12 @@ impl BeaconNodeHttpClient {
}
/// `GET v2/validator/blinded_blocks/{slot}`
pub async fn get_validator_blinded_blocks<T: EthSpec, Payload: AbstractExecPayload<T>>(
pub async fn get_validator_blinded_blocks<T: EthSpec>(
&self,
slot: Slot,
randao_reveal: &SignatureBytes,
graffiti: Option<&Graffiti>,
) -> Result<ForkVersionedResponse<BlockContents<T, Payload>>, Error> {
) -> Result<ForkVersionedResponse<BlindedBeaconBlock<T>>, Error> {
self.get_validator_blinded_blocks_modular(
slot,
randao_reveal,
@@ -1959,7 +2007,7 @@ impl BeaconNodeHttpClient {
}
/// returns `GET v1/validator/blinded_blocks/{slot}` URL path
pub async fn get_validator_blinded_blocks_path<T: EthSpec, Payload: AbstractExecPayload<T>>(
pub async fn get_validator_blinded_blocks_path<T: EthSpec>(
&self,
slot: Slot,
randao_reveal: &SignatureBytes,
@@ -1991,18 +2039,15 @@ impl BeaconNodeHttpClient {
}
/// `GET v1/validator/blinded_blocks/{slot}`
pub async fn get_validator_blinded_blocks_modular<
T: EthSpec,
Payload: AbstractExecPayload<T>,
>(
pub async fn get_validator_blinded_blocks_modular<T: EthSpec>(
&self,
slot: Slot,
randao_reveal: &SignatureBytes,
graffiti: Option<&Graffiti>,
skip_randao_verification: SkipRandaoVerification,
) -> Result<ForkVersionedResponse<BlockContents<T, Payload>>, Error> {
) -> Result<ForkVersionedResponse<BlindedBeaconBlock<T>>, Error> {
let path = self
.get_validator_blinded_blocks_path::<T, Payload>(
.get_validator_blinded_blocks_path::<T>(
slot,
randao_reveal,
graffiti,
@@ -2014,13 +2059,13 @@ impl BeaconNodeHttpClient {
}
/// `GET v2/validator/blinded_blocks/{slot}` in ssz format
pub async fn get_validator_blinded_blocks_ssz<T: EthSpec, Payload: AbstractExecPayload<T>>(
pub async fn get_validator_blinded_blocks_ssz<T: EthSpec>(
&self,
slot: Slot,
randao_reveal: &SignatureBytes,
graffiti: Option<&Graffiti>,
) -> Result<Option<Vec<u8>>, Error> {
self.get_validator_blinded_blocks_modular_ssz::<T, Payload>(
self.get_validator_blinded_blocks_modular_ssz::<T>(
slot,
randao_reveal,
graffiti,
@@ -2029,10 +2074,7 @@ impl BeaconNodeHttpClient {
.await
}
pub async fn get_validator_blinded_blocks_modular_ssz<
T: EthSpec,
Payload: AbstractExecPayload<T>,
>(
pub async fn get_validator_blinded_blocks_modular_ssz<T: EthSpec>(
&self,
slot: Slot,
randao_reveal: &SignatureBytes,
@@ -2040,7 +2082,7 @@ impl BeaconNodeHttpClient {
skip_randao_verification: SkipRandaoVerification,
) -> Result<Option<Vec<u8>>, Error> {
let path = self
.get_validator_blinded_blocks_path::<T, Payload>(
.get_validator_blinded_blocks_path::<T>(
slot,
randao_reveal,
graffiti,
@@ -2150,7 +2192,7 @@ impl BeaconNodeHttpClient {
pub async fn post_validator_liveness_epoch(
&self,
epoch: Epoch,
indices: Vec<u64>,
indices: &Vec<u64>,
) -> Result<GenericResponse<Vec<StandardLivenessResponseData>>, Error> {
let mut path = self.eth_path(V1)?;
@@ -2160,7 +2202,7 @@ impl BeaconNodeHttpClient {
.push("liveness")
.push(&epoch.to_string());
self.post_with_timeout_and_response(path, &indices, self.timeouts.liveness)
self.post_with_timeout_and_response(path, indices, self.timeouts.liveness)
.await
}

View File

@@ -226,11 +226,32 @@ impl ValidatorClientHttpClient {
ok_or_error(response).await
}
/// Perform a HTTP DELETE request, returning the `Response` for further processing.
async fn delete_response<U: IntoUrl>(&self, url: U) -> Result<Response, Error> {
let response = self
.client
.delete(url)
.headers(self.headers()?)
.send()
.await
.map_err(Error::from)?;
ok_or_error(response).await
}
async fn get<T: DeserializeOwned, U: IntoUrl>(&self, url: U) -> Result<T, Error> {
let response = self.get_response(url).await?;
self.signed_json(response).await
}
async fn delete<U: IntoUrl>(&self, url: U) -> Result<(), Error> {
let response = self.delete_response(url).await?;
if response.status().is_success() {
Ok(())
} else {
Err(Error::StatusCode(response.status()))
}
}
async fn get_unsigned<T: DeserializeOwned, U: IntoUrl>(&self, url: U) -> Result<T, Error> {
self.get_response(url)
.await?
@@ -537,6 +558,18 @@ impl ValidatorClientHttpClient {
Ok(url)
}
fn make_graffiti_url(&self, pubkey: &PublicKeyBytes) -> Result<Url, Error> {
let mut url = self.server.full.clone();
url.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
.push("eth")
.push("v1")
.push("validator")
.push(&pubkey.to_string())
.push("graffiti");
Ok(url)
}
fn make_gas_limit_url(&self, pubkey: &PublicKeyBytes) -> Result<Url, Error> {
let mut url = self.server.full.clone();
url.path_segments_mut()
@@ -684,6 +717,34 @@ impl ValidatorClientHttpClient {
self.post(path, &()).await
}
/// `GET /eth/v1/validator/{pubkey}/graffiti`
pub async fn get_graffiti(
&self,
pubkey: &PublicKeyBytes,
) -> Result<GetGraffitiResponse, Error> {
let url = self.make_graffiti_url(pubkey)?;
self.get(url)
.await
.map(|generic: GenericResponse<GetGraffitiResponse>| generic.data)
}
/// `POST /eth/v1/validator/{pubkey}/graffiti`
pub async fn set_graffiti(
&self,
pubkey: &PublicKeyBytes,
graffiti: GraffitiString,
) -> Result<(), Error> {
let url = self.make_graffiti_url(pubkey)?;
let set_graffiti_request = SetGraffitiRequest { graffiti };
self.post(url, &set_graffiti_request).await
}
/// `DELETE /eth/v1/validator/{pubkey}/graffiti`
pub async fn delete_graffiti(&self, pubkey: &PublicKeyBytes) -> Result<(), Error> {
let url = self.make_graffiti_url(pubkey)?;
self.delete(url).await
}
}
/// Returns `Ok(response)` if the response is a `200 OK` response or a

View File

@@ -1,7 +1,7 @@
use account_utils::ZeroizeString;
use eth2_keystore::Keystore;
use serde::{Deserialize, Serialize};
use types::{Address, PublicKeyBytes};
use types::{Address, Graffiti, PublicKeyBytes};
pub use slashing_protection::interchange::Interchange;
@@ -172,3 +172,9 @@ pub enum DeleteRemotekeyStatus {
pub struct DeleteRemotekeysResponse {
pub data: Vec<Status<DeleteRemotekeyStatus>>,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct GetGraffitiResponse {
pub pubkey: PublicKeyBytes,
pub graffiti: Graffiti,
}

View File

@@ -168,3 +168,8 @@ pub struct SingleExportKeystoresResponse {
#[serde(skip_serializing_if = "Option::is_none")]
pub validating_keystore_password: Option<ZeroizeString>,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct SetGraffitiRequest {
pub graffiti: GraffitiString,
}

View File

@@ -12,9 +12,7 @@ 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")]
@@ -280,17 +278,18 @@ pub struct FinalityCheckpointsData {
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(try_from = "&str")]
#[serde(into = "String")]
#[serde(try_from = "std::borrow::Cow<str>")]
pub enum ValidatorId {
PublicKey(PublicKeyBytes),
Index(u64),
}
impl TryFrom<&str> for ValidatorId {
impl TryFrom<std::borrow::Cow<'_, str>> for ValidatorId {
type Error = String;
fn try_from(s: &str) -> Result<Self, Self::Error> {
Self::from_str(s)
fn try_from(s: std::borrow::Cow<str>) -> Result<Self, Self::Error> {
Self::from_str(&s)
}
}
@@ -319,6 +318,12 @@ impl fmt::Display for ValidatorId {
}
}
impl From<ValidatorId> for String {
fn from(id: ValidatorId) -> String {
id.to_string()
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ValidatorData {
#[serde(with = "serde_utils::quoted_u64")]
@@ -494,6 +499,15 @@ pub struct ValidatorsQuery {
pub status: Option<Vec<ValidatorStatus>>,
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ValidatorsRequestBody {
#[serde(default)]
pub ids: Option<Vec<ValidatorId>>,
#[serde(default)]
pub statuses: Option<Vec<ValidatorStatus>>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CommitteeData {
#[serde(with = "serde_utils::quoted_u64")]
@@ -658,6 +672,12 @@ pub struct ValidatorBalancesQuery {
pub id: Option<Vec<ValidatorId>>,
}
#[derive(Clone, Serialize, Deserialize)]
#[serde(transparent)]
pub struct ValidatorBalancesRequestBody {
pub ids: Vec<ValidatorId>,
}
#[derive(Clone, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct BlobIndicesQuery {
@@ -901,9 +921,9 @@ pub struct SseBlobSidecar {
impl SseBlobSidecar {
pub fn from_blob_sidecar<E: EthSpec>(blob_sidecar: &BlobSidecar<E>) -> SseBlobSidecar {
SseBlobSidecar {
block_root: blob_sidecar.block_root,
block_root: blob_sidecar.block_root(),
index: blob_sidecar.index,
slot: blob_sidecar.slot,
slot: blob_sidecar.slot(),
kzg_commitment: blob_sidecar.kzg_commitment,
versioned_hash: blob_sidecar.kzg_commitment.calculate_versioned_hash(),
}
@@ -1411,15 +1431,14 @@ pub mod serde_status_code {
}
pub enum ForkVersionedBeaconBlockType<T: EthSpec> {
Full(ForkVersionedResponse<BlockContents<T, FullPayload<T>>>),
Blinded(ForkVersionedResponse<BlockContents<T, BlindedPayload<T>>>),
Full(ForkVersionedResponse<FullBlockContents<T>>),
Blinded(ForkVersionedResponse<BlindedBeaconBlock<T>>),
}
#[cfg(test)]
mod tests {
use super::*;
use ssz::Encode;
use std::sync::Arc;
#[test]
fn query_vec() {
@@ -1460,17 +1479,17 @@ mod tests {
type E = MainnetEthSpec;
let spec = ForkName::Capella.make_genesis_spec(E::default_spec());
let block: SignedBlockContents<E, FullPayload<E>> = SignedBeaconBlock::from_block(
let block: PublishBlockRequest<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)
let decoded: PublishBlockRequest<E> =
PublishBlockRequest::from_ssz_bytes(&block.as_ssz_bytes(), &spec)
.expect("should decode Block");
assert!(matches!(decoded, SignedBlockContents::Block(_)));
assert!(matches!(decoded, PublishBlockRequest::Block(_)));
}
#[test]
@@ -1482,87 +1501,49 @@ mod tests {
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 blobs = BlobsList::<E>::from(vec![Blob::<E>::default()]);
let kzg_proofs = KzgProofs::<E>::from(vec![KzgProof::empty()]);
let signed_block_contents = PublishBlockRequest::new(block, Some((kzg_proofs, blobs)));
let decoded: SignedBlockContents<E, FullPayload<E>> =
SignedBlockContents::from_ssz_bytes(&signed_block_contents.as_ssz_bytes(), &spec)
let decoded: PublishBlockRequest<E> =
PublishBlockRequest::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(_)
));
assert!(matches!(decoded, PublishBlockRequest::BlockContents(_)));
}
}
/// A wrapper over a [`BeaconBlock`] or a [`BeaconBlockAndBlobSidecars`].
#[derive(Debug, Encode, Serialize, Deserialize)]
#[serde(untagged)]
#[serde(bound = "E: EthSpec")]
#[ssz(enum_behaviour = "transparent")]
pub enum ProduceBlockV3Response<E: EthSpec> {
Full(FullBlockContents<E>),
Blinded(BlindedBeaconBlock<E>),
}
/// A wrapper over a [`BeaconBlock`] or a [`BlockContents`].
#[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 enum FullBlockContents<T: EthSpec> {
/// This is a full deneb variant with block and blobs.
BlockContents(BlockContents<T>),
/// This variant is for all pre-deneb full blocks.
Block(BeaconBlock<T>),
}
pub type BlockContentsTuple<T, Payload> = (
BeaconBlock<T, Payload>,
Option<SidecarList<T, <Payload as AbstractExecPayload<T>>::Sidecar>>,
);
pub type BlockContentsTuple<T> = (BeaconBlock<T>, Option<(KzgProofs<T>, BlobsList<T>)>);
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),
impl<T: EthSpec> FullBlockContents<T> {
pub fn new(block: BeaconBlock<T>, blob_data: Option<(KzgProofs<T>, BlobsList<T>)>) -> Self {
match blob_data {
Some((kzg_proofs, blobs)) => Self::BlockContents(BlockContents {
block,
kzg_proofs,
blobs,
}),
None => Self::Block(block),
}
}
@@ -1581,43 +1562,41 @@ impl<T: EthSpec, Payload: AbstractExecPayload<T>> BlockContents<T, Payload> {
match fork_at_slot {
ForkName::Base | ForkName::Altair | ForkName::Merge | ForkName::Capella => {
BeaconBlock::from_ssz_bytes(bytes, spec).map(|block| BlockContents::Block(block))
BeaconBlock::from_ssz_bytes(bytes, spec)
.map(|block| FullBlockContents::Block(block))
}
ForkName::Deneb => {
let mut builder = ssz::SszDecoderBuilder::new(bytes);
builder.register_anonymous_variable_length_item()?;
builder.register_type::<SidecarList<T, Payload::Sidecar>>()?;
builder.register_type::<KzgProofs<T>>()?;
builder.register_type::<BlobsList<T>>()?;
let mut decoder = builder.build()?;
let block =
decoder.decode_next_with(|bytes| BeaconBlock::from_ssz_bytes(bytes, spec))?;
let kzg_proofs = decoder.decode_next()?;
let blobs = decoder.decode_next()?;
Ok(BlockContents::new(block, Some(blobs)))
Ok(FullBlockContents::new(block, Some((kzg_proofs, blobs))))
}
}
}
pub fn block(&self) -> &BeaconBlock<T, Payload> {
pub fn block(&self) -> &BeaconBlock<T> {
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,
FullBlockContents::BlockContents(block_and_sidecars) => &block_and_sidecars.block,
FullBlockContents::Block(block) => block,
}
}
pub fn deconstruct(self) -> BlockContentsTuple<T, Payload> {
pub fn deconstruct(self) -> BlockContentsTuple<T> {
match self {
BlockContents::BlockAndBlobSidecars(block_and_sidecars) => (
FullBlockContents::BlockContents(block_and_sidecars) => (
block_and_sidecars.block,
Some(block_and_sidecars.blob_sidecars),
Some((block_and_sidecars.kzg_proofs, block_and_sidecars.blobs)),
),
BlockContents::BlindedBlockAndBlobSidecars(block_and_sidecars) => (
block_and_sidecars.blinded_block,
Some(block_and_sidecars.blinded_blob_sidecars),
),
BlockContents::Block(block) => (block, None),
FullBlockContents::Block(block) => (block, None),
}
}
@@ -1628,104 +1607,64 @@ impl<T: EthSpec, Payload: AbstractExecPayload<T>> BlockContents<T, Payload> {
fork: &Fork,
genesis_validators_root: Hash256,
spec: &ChainSpec,
) -> SignedBlockContents<T, Payload> {
) -> PublishBlockRequest<T> {
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)
PublishBlockRequest::new(signed_block, maybe_blobs)
}
}
impl<T: EthSpec, Payload: AbstractExecPayload<T>> ForkVersionDeserialize
for BlockContents<T, Payload>
{
impl<T: EthSpec> ForkVersionDeserialize for FullBlockContents<T> {
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)
Ok(FullBlockContents::Block(
BeaconBlock::deserialize_by_fork::<'de, D>(value, fork_name)?,
))
}
ForkName::Deneb => Ok(FullBlockContents::BlockContents(
BlockContents::deserialize_by_fork::<'de, D>(value, fork_name)?,
)),
}
}
}
impl<T: EthSpec, Payload: AbstractExecPayload<T>> Into<BeaconBlock<T, Payload>>
for BlockContents<T, Payload>
{
fn into(self) -> BeaconBlock<T, Payload> {
impl<T: EthSpec> Into<BeaconBlock<T>> for FullBlockContents<T> {
fn into(self) -> BeaconBlock<T> {
match self {
Self::BlockAndBlobSidecars(block_and_sidecars) => block_and_sidecars.block,
Self::BlindedBlockAndBlobSidecars(block_and_sidecars) => {
block_and_sidecars.blinded_block
}
Self::BlockContents(block_and_sidecars) => block_and_sidecars.block,
Self::Block(block) => block,
}
}
}
pub type SignedBlockContentsTuple<T, Payload> = (
SignedBeaconBlock<T, Payload>,
Option<SignedSidecarList<T, <Payload as AbstractExecPayload<T>>::Sidecar>>,
);
pub type SignedBlockContentsTuple<T> = (SignedBeaconBlock<T>, Option<(KzgProofs<T>, BlobsList<T>)>);
pub type SignedBlindedBlockContents<E> = SignedBlockContents<E, BlindedPayload<E>>;
/// A wrapper over a [`SignedBeaconBlock`] or a [`SignedBeaconBlockAndBlobSidecars`].
/// A wrapper over a [`SignedBeaconBlock`] or a [`SignedBlockContents`].
#[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>),
pub enum PublishBlockRequest<T: EthSpec> {
BlockContents(SignedBlockContents<T>),
Block(SignedBeaconBlock<T>),
}
impl<T: EthSpec, Payload: AbstractExecPayload<T>> SignedBlockContents<T, Payload> {
impl<T: EthSpec> PublishBlockRequest<T> {
pub fn new(
block: SignedBeaconBlock<T, Payload>,
blobs: Option<SignedSidecarList<T, Payload::Sidecar>>,
block: SignedBeaconBlock<T>,
blob_items: Option<(KzgProofs<T>, BlobsList<T>)>,
) -> 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),
match blob_items {
Some((kzg_proofs, blobs)) => Self::BlockContents(SignedBlockContents {
signed_block: block,
kzg_proofs,
blobs,
}),
None => Self::Block(block),
}
}
@@ -1745,133 +1684,88 @@ impl<T: EthSpec, Payload: AbstractExecPayload<T>> SignedBlockContents<T, Payload
match fork_at_slot {
ForkName::Base | ForkName::Altair | ForkName::Merge | ForkName::Capella => {
SignedBeaconBlock::from_ssz_bytes(bytes, spec)
.map(|block| SignedBlockContents::Block(block))
.map(|block| PublishBlockRequest::Block(block))
}
ForkName::Deneb => {
let mut builder = ssz::SszDecoderBuilder::new(bytes);
builder.register_anonymous_variable_length_item()?;
builder.register_type::<SignedSidecarList<T, Payload::Sidecar>>()?;
builder.register_type::<KzgProofs<T>>()?;
builder.register_type::<BlobsList<T>>()?;
let mut decoder = builder.build()?;
let block = decoder
.decode_next_with(|bytes| SignedBeaconBlock::from_ssz_bytes(bytes, spec))?;
let kzg_proofs = decoder.decode_next()?;
let blobs = decoder.decode_next()?;
Ok(SignedBlockContents::new(block, Some(blobs)))
Ok(PublishBlockRequest::new(block, Some((kzg_proofs, blobs))))
}
}
}
pub fn signed_block(&self) -> &SignedBeaconBlock<T, Payload> {
pub fn signed_block(&self) -> &SignedBeaconBlock<T> {
match self {
SignedBlockContents::BlockAndBlobSidecars(block_and_sidecars) => {
PublishBlockRequest::BlockContents(block_and_sidecars) => {
&block_and_sidecars.signed_block
}
SignedBlockContents::BlindedBlockAndBlobSidecars(block_and_sidecars) => {
&block_and_sidecars.signed_blinded_block
}
SignedBlockContents::Block(block) => block,
PublishBlockRequest::Block(block) => block,
}
}
pub fn blobs_cloned(&self) -> Option<SignedSidecarList<T, Payload::Sidecar>> {
pub fn deconstruct(self) -> SignedBlockContentsTuple<T> {
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) => (
PublishBlockRequest::BlockContents(block_and_sidecars) => (
block_and_sidecars.signed_block,
Some(block_and_sidecars.signed_blob_sidecars),
Some((block_and_sidecars.kzg_proofs, block_and_sidecars.blobs)),
),
SignedBlockContents::BlindedBlockAndBlobSidecars(block_and_sidecars) => (
block_and_sidecars.signed_blinded_block,
Some(block_and_sidecars.signed_blinded_blob_sidecars),
),
SignedBlockContents::Block(block) => (block, None),
PublishBlockRequest::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();
/// Converting from a `SignedBlindedBeaconBlock` into a full `SignedBlockContents`.
pub fn into_full_block_and_blobs<T: EthSpec>(
blinded_block: SignedBlindedBeaconBlock<T>,
maybe_full_payload_contents: Option<FullPayloadContents<T>>,
) -> Result<PublishBlockRequest<T>, String> {
match maybe_full_payload_contents {
None => {
let signed_block = blinded_block
.try_into_full_block(None)
.ok_or("Failed to build full block with payload".to_string())?;
Ok(PublishBlockRequest::new(signed_block, None))
}
// This variant implies a pre-deneb block
Some(FullPayloadContents::Payload(execution_payload)) => {
let signed_block = blinded_block
.try_into_full_block(Some(execution_payload))
.ok_or("Failed to build full block with payload".to_string())?;
Ok(PublishBlockRequest::new(signed_block, None))
}
// This variant implies a post-deneb block
Some(FullPayloadContents::PayloadAndBlobs(payload_and_blobs)) => {
let signed_block = blinded_block
.try_into_full_block(Some(payload_and_blobs.execution_payload))
.ok_or("Failed to build full block with payload".to_string())?;
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(),
),
Ok(PublishBlockRequest::new(
signed_block,
Some((
payload_and_blobs.blobs_bundle.proofs,
payload_and_blobs.blobs_bundle.blobs,
)),
))
}
}
}
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>
{
impl<T: EthSpec> TryFrom<SignedBeaconBlock<T>> for PublishBlockRequest<T> {
type Error = &'static str;
fn try_from(block: SignedBeaconBlock<T, Payload>) -> Result<Self, Self::Error> {
fn try_from(block: SignedBeaconBlock<T>) -> Result<Self, Self::Error> {
match block {
SignedBeaconBlock::Base(_)
| SignedBeaconBlock::Altair(_)
| SignedBeaconBlock::Merge(_)
| SignedBeaconBlock::Capella(_) => Ok(SignedBlockContents::Block(block)),
| SignedBeaconBlock::Capella(_) => Ok(PublishBlockRequest::Block(block)),
SignedBeaconBlock::Deneb(_) => {
Err("deneb block contents cannot be fully constructed from just the signed block")
}
@@ -1879,93 +1773,49 @@ impl<T: EthSpec, Payload: AbstractExecPayload<T>> TryFrom<SignedBeaconBlock<T, P
}
}
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)
impl<T: EthSpec> From<SignedBlockContentsTuple<T>> for PublishBlockRequest<T> {
fn from(block_contents_tuple: SignedBlockContentsTuple<T>) -> Self {
PublishBlockRequest::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>,
pub struct SignedBlockContents<T: EthSpec> {
pub signed_block: SignedBeaconBlock<T>,
pub kzg_proofs: KzgProofs<T>,
#[serde(with = "ssz_types::serde_utils::list_of_hex_fixed_vec")]
pub blobs: BlobsList<T>,
}
#[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>,
#[serde(bound = "T: EthSpec")]
pub struct BlockContents<T: EthSpec> {
pub block: BeaconBlock<T>,
pub kzg_proofs: KzgProofs<T>,
#[serde(with = "ssz_types::serde_utils::list_of_hex_fixed_vec")]
pub blobs: BlobsList<T>,
}
impl<T: EthSpec, Payload: AbstractExecPayload<T>> ForkVersionDeserialize
for BeaconBlockAndBlobSidecars<T, Payload>
{
impl<T: EthSpec> ForkVersionDeserialize for BlockContents<T> {
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>> {
#[serde(bound = "T: EthSpec")]
struct Helper<T: EthSpec> {
block: serde_json::Value,
blob_sidecars: SidecarList<T, S>,
kzg_proofs: KzgProofs<T>,
#[serde(with = "ssz_types::serde_utils::list_of_hex_fixed_vec")]
blobs: BlobsList<T>,
}
let helper: Helper<T, Payload::Sidecar> =
serde_json::from_value(value).map_err(serde::de::Error::custom)?;
let helper: Helper<T> = 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,
kzg_proofs: helper.kzg_proofs,
blobs: helper.blobs,
})
}
}
@@ -2052,17 +1902,19 @@ pub struct BlobsBundle<E: EthSpec> {
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(),
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn validator_id_serde() {
let id_str = "\"1\"";
let x: ValidatorId = serde_json::from_str(id_str).unwrap();
assert_eq!(x, ValidatorId::Index(1));
assert_eq!(serde_json::to_string(&x).unwrap(), id_str);
let pubkey_str = "\"0xb824b5ede33a7b05a378a84b183b4bc7e7db894ce48b659f150c97d359edca2f503081d6678d1200f582ec7cafa9caf2\"";
let y: ValidatorId = serde_json::from_str(pubkey_str).unwrap();
assert_eq!(serde_json::to_string(&y).unwrap(), pubkey_str);
}
}