Implement SSZ decoding for SignedBlockContents (#4744)

* Implement `SignedBlockContent` decoding and fixed bug in `SignedBlockContent::new`

* Update Cargo.lock file

* Use `make_genesis_spec` to simplify test setup.

* Fix syntax errors.
This commit is contained in:
Jimmy Chen 2023-09-20 08:18:05 +10:00 committed by GitHub
parent 5f98a7b8ad
commit 665334e936
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 123 additions and 24 deletions

4
Cargo.lock generated
View File

@ -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]]

View File

@ -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<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`].
@ -1524,13 +1599,13 @@ impl<T: EthSpec, Payload: AbstractExecPayload<T>> SignedBlockContents<T, Payload
blobs: Option<SignedSidecarList<T, Payload::Sidecar>>,
) -> 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,10 +1617,35 @@ impl<T: EthSpec, Payload: AbstractExecPayload<T>> SignedBlockContents<T, Payload
/// SSZ decode with fork variant determined by slot.
pub fn from_ssz_bytes(bytes: &[u8], spec: &ChainSpec) -> Result<Self, ssz::DecodeError> {
// FIXME(jimmy): SSZ decode not implemented for `SignedBeaconBlockAndBlobSidecars`
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 {
@ -1669,23 +1769,7 @@ impl<T: EthSpec, Payload: AbstractExecPayload<T>> From<SignedBlockContentsTuple<
for SignedBlockContents<T, Payload>
{
fn from(block_contents_tuple: SignedBlockContentsTuple<T, Payload>) -> 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)
}
}

View File

@ -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<T, Sidecar> = VariableList<Arc<Sidecar>, <T as EthSpec>::MaxBlobsPerBlock>;