diff --git a/Cargo.lock b/Cargo.lock index f0f5d1d117..4c62adb5f1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -753,7 +753,7 @@ version = "0.66.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2b84e06fc203107bfbad243f4aba2af864eb7db3b1cf46ea0a023b0b433d2a7" dependencies = [ - "bitflags 2.3.3", + "bitflags 2.4.0", "cexpr", "clang-sys", "lazy_static", @@ -6165,7 +6165,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c64d9ba0963cdcea2e1b2230fbae2bab30eb25a174be395c41e764bfb65dd62" dependencies = [ "proc-macro2", - "syn 2.0.28", + "syn 2.0.29", ] [[package]] diff --git a/common/eth2/src/types.rs b/common/eth2/src/types.rs index 53b57f456f..1c1b956bd1 100644 --- a/common/eth2/src/types.rs +++ b/common/eth2/src/types.rs @@ -6,6 +6,7 @@ use lighthouse_network::{ConnectionDirection, Enr, Multiaddr, PeerConnectionStat use mediatype::{names, MediaType, MediaTypeList}; use serde::{Deserialize, Deserializer, Serialize}; use serde_json::Value; +use ssz::{Decode, DecodeError}; use ssz_derive::Encode; use std::convert::TryFrom; use std::fmt::{self, Display}; @@ -1356,6 +1357,8 @@ pub mod serde_status_code { #[cfg(test)] mod tests { use super::*; + use ssz::Encode; + use std::sync::Arc; #[test] fn query_vec() { @@ -1390,6 +1393,78 @@ 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> = SignedBeaconBlock::from_block( + BeaconBlock::::Capella(BeaconBlockCapella::empty(&spec)), + Signature::empty(), + ) + .try_into() + .expect("should convert into signed block contents"); + + let decoded: SignedBlockContents = + 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::::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> = + 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::>::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> = + 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`]. @@ -1524,13 +1599,13 @@ impl> SignedBlockContents>, ) -> Self { match (Payload::block_type(), blobs) { - (BlockType::Blinded, Some(blobs)) => { + (BlockType::Full, Some(blobs)) => { Self::BlockAndBlobSidecars(SignedBeaconBlockAndBlobSidecars { signed_block: block, signed_blob_sidecars: blobs, }) } - (BlockType::Full, Some(blobs)) => { + (BlockType::Blinded, Some(blobs)) => { Self::BlindedBlockAndBlobSidecars(SignedBlindedBeaconBlockAndBlobSidecars { signed_blinded_block: block, signed_blinded_blob_sidecars: blobs, @@ -1542,9 +1617,34 @@ impl> SignedBlockContents Result { - // FIXME(jimmy): SSZ decode not implemented for `SignedBeaconBlockAndBlobSidecars` - SignedBeaconBlock::from_ssz_bytes(bytes, spec) - .map(|block| SignedBlockContents::Block(block)) + let slot_len = ::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::(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::>()?; + + 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 { @@ -1669,23 +1769,7 @@ impl> From { fn from(block_contents_tuple: SignedBlockContentsTuple) -> Self { - match block_contents_tuple { - (signed_block, None) => SignedBlockContents::Block(signed_block), - (signed_block, Some(signed_blob_sidecars)) => match Payload::block_type() { - BlockType::Blinded => SignedBlockContents::BlindedBlockAndBlobSidecars( - SignedBlindedBeaconBlockAndBlobSidecars { - signed_blinded_block: signed_block, - signed_blinded_blob_sidecars: signed_blob_sidecars, - }, - ), - BlockType::Full => { - SignedBlockContents::BlockAndBlobSidecars(SignedBeaconBlockAndBlobSidecars { - signed_block, - signed_blob_sidecars, - }) - } - }, - } + SignedBlockContents::new(block_contents_tuple.0, block_contents_tuple.1) } } diff --git a/consensus/types/src/blob_sidecar.rs b/consensus/types/src/blob_sidecar.rs index 97233530f9..aedb2cde82 100644 --- a/consensus/types/src/blob_sidecar.rs +++ b/consensus/types/src/blob_sidecar.rs @@ -197,6 +197,21 @@ pub struct BlindedBlobSidecar { pub kzg_proof: KzgProof, } +impl BlindedBlobSidecar { + pub fn empty() -> Self { + Self { + block_root: Hash256::zero(), + index: 0, + slot: Slot::new(0), + block_parent_root: Hash256::zero(), + proposer_index: 0, + blob_root: Hash256::zero(), + kzg_commitment: KzgCommitment::empty_for_testing(), + kzg_proof: KzgProof::empty(), + } + } +} + impl SignedRoot for BlindedBlobSidecar {} pub type SidecarList = VariableList, ::MaxBlobsPerBlock>;