From 8de9157716de9eaf5fd598c7a1dec7628c85cdd6 Mon Sep 17 00:00:00 2001 From: Eitan Seri-Levi Date: Mon, 28 Jul 2025 09:55:09 -0700 Subject: [PATCH 1/4] fix ssz formatting --- beacon_node/http_api/src/light_client.rs | 23 ++++++++------ beacon_node/http_api/tests/tests.rs | 20 ++++++++++++ common/eth2/src/lib.rs | 26 +++++++++++++++ common/eth2/src/types.rs | 40 ++++++++++++++++++++---- 4 files changed, 93 insertions(+), 16 deletions(-) diff --git a/beacon_node/http_api/src/light_client.rs b/beacon_node/http_api/src/light_client.rs index f9559d738ea..1b97191bdc1 100644 --- a/beacon_node/http_api/src/light_client.rs +++ b/beacon_node/http_api/src/light_client.rs @@ -34,13 +34,15 @@ pub fn get_light_client_updates( match accept_header { Some(api_types::Accept::Ssz) => { let response_chunks = light_client_updates - .iter() - .map(|update| map_light_client_update_to_ssz_chunk::(&chain, update)) - .collect::>(); + .into_iter() + .flat_map(|update| { + map_light_client_update_to_response_chunk::(&chain, update).as_ssz_bytes() + }) + .collect(); Response::builder() .status(200) - .body(response_chunks.as_ssz_bytes()) + .body(response_chunks) .map(|res: Response>| add_ssz_content_type_header(res)) .map_err(|e| { warp_utils::reject::custom_server_error(format!( @@ -146,23 +148,24 @@ pub fn validate_light_client_updates_request( Ok(()) } -fn map_light_client_update_to_ssz_chunk( +fn map_light_client_update_to_response_chunk( chain: &BeaconChain, - light_client_update: &LightClientUpdate, -) -> LightClientUpdateResponseChunk { + light_client_update: LightClientUpdate, +) -> LightClientUpdateResponseChunk { let epoch = light_client_update .attested_header_slot() .epoch(T::EthSpec::slots_per_epoch()); let fork_digest = chain.compute_fork_digest(epoch); - let payload = light_client_update.as_ssz_bytes(); - let response_chunk_len = fork_digest.len() + payload.len(); + let response_chunk_len = fork_digest.len() + light_client_update.ssz_bytes_len(); let response_chunk = LightClientUpdateResponseChunkInner { context: fork_digest, - payload, + payload: light_client_update, }; + println!("response chunk len {:?}", response_chunk_len); + LightClientUpdateResponseChunk { response_chunk_len: response_chunk_len as u64, response_chunk, diff --git a/beacon_node/http_api/tests/tests.rs b/beacon_node/http_api/tests/tests.rs index 5ac8cd91864..cceb1566468 100644 --- a/beacon_node/http_api/tests/tests.rs +++ b/beacon_node/http_api/tests/tests.rs @@ -2213,6 +2213,24 @@ impl ApiTester { self } + pub async fn test_get_beacon_light_client_updates_ssz(self) -> Self { + let current_epoch = self.chain.epoch().unwrap(); + let current_sync_committee_period = current_epoch + .sync_committee_period(&self.chain.spec) + .unwrap(); + + match self + .client + .get_beacon_light_client_updates_ssz::(current_sync_committee_period, 1) + .await + { + Ok(result) => result, + Err(e) => panic!("query failed incorrectly: {e:?}"), + }; + + self + } + pub async fn test_get_beacon_light_client_updates(self) -> Self { let current_epoch = self.chain.epoch().unwrap(); let current_sync_committee_period = current_epoch @@ -7048,6 +7066,8 @@ async fn get_light_client_updates() { ApiTester::new_from_config(config) .await .test_get_beacon_light_client_updates() + .await + .test_get_beacon_light_client_updates_ssz() .await; } diff --git a/common/eth2/src/lib.rs b/common/eth2/src/lib.rs index a129f9c4fa5..6b0c6824fd8 100644 --- a/common/eth2/src/lib.rs +++ b/common/eth2/src/lib.rs @@ -981,6 +981,32 @@ impl BeaconNodeHttpClient { }) } + /// `GET beacon/light_client/updates` + /// + /// Returns `Ok(None)` on a 404 error. + pub async fn get_beacon_light_client_updates_ssz( + &self, + start_period: u64, + count: u64, + ) -> Result>, Error> { + let mut path = self.eth_path(V1)?; + + path.path_segments_mut() + .map_err(|()| Error::InvalidUrl(self.server.clone()))? + .push("beacon") + .push("light_client") + .push("updates"); + + path.query_pairs_mut() + .append_pair("start_period", &start_period.to_string()); + + path.query_pairs_mut() + .append_pair("count", &count.to_string()); + + self.get_bytes_opt_accept_header(path, Accept::Ssz, self.timeouts.get_beacon_blocks_ssz) + .await + } + /// `GET beacon/light_client/bootstrap` /// /// Returns `Ok(None)` on a 404 error. diff --git a/common/eth2/src/types.rs b/common/eth2/src/types.rs index 07b5cb50166..1186c318ab6 100644 --- a/common/eth2/src/types.rs +++ b/common/eth2/src/types.rs @@ -11,6 +11,7 @@ use multiaddr::Multiaddr; use reqwest::header::HeaderMap; use serde::{Deserialize, Deserializer, Serialize}; use serde_utils::quoted_u64::Quoted; +use ssz::Encode; use ssz::{Decode, DecodeError}; use ssz_derive::{Decode, Encode}; use std::fmt::{self, Display}; @@ -823,16 +824,43 @@ pub struct LightClientUpdatesQuery { pub count: u64, } -#[derive(Encode, Decode)] -pub struct LightClientUpdateResponseChunk { +pub struct LightClientUpdateResponseChunk { pub response_chunk_len: u64, - pub response_chunk: LightClientUpdateResponseChunkInner, + pub response_chunk: LightClientUpdateResponseChunkInner, } -#[derive(Encode, Decode)] -pub struct LightClientUpdateResponseChunkInner { +impl Encode for LightClientUpdateResponseChunk { + fn is_ssz_fixed_len() -> bool { + false + } + + fn ssz_bytes_len(&self) -> usize { + 0_u64.ssz_bytes_len() + self.response_chunk.context.len() + + match &self.response_chunk.payload { + LightClientUpdate::Altair(lc) => lc.ssz_bytes_len(), + LightClientUpdate::Capella(lc) => lc.ssz_bytes_len(), + LightClientUpdate::Deneb(lc) => lc.ssz_bytes_len(), + LightClientUpdate::Electra(lc) => lc.ssz_bytes_len(), + LightClientUpdate::Fulu(lc) => lc.ssz_bytes_len(), + } + } + + fn ssz_append(&self, buf: &mut Vec) { + buf.extend_from_slice(&self.response_chunk_len.to_le_bytes()); + buf.extend_from_slice(&self.response_chunk.context); + match &self.response_chunk.payload { + LightClientUpdate::Altair(lc) => lc.ssz_append(buf), + LightClientUpdate::Capella(lc) => lc.ssz_append(buf), + LightClientUpdate::Deneb(lc) => lc.ssz_append(buf), + LightClientUpdate::Electra(lc) => lc.ssz_append(buf), + LightClientUpdate::Fulu(lc) => lc.ssz_append(buf), + }; + } +} + +pub struct LightClientUpdateResponseChunkInner { pub context: [u8; 4], - pub payload: Vec, + pub payload: LightClientUpdate, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash)] From 0165b6eb83a88bb449d4dd95212497116773f9fa Mon Sep 17 00:00:00 2001 From: Eitan Seri-Levi Date: Tue, 29 Jul 2025 11:24:07 -0700 Subject: [PATCH 2/4] fmt --- common/eth2/src/types.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/common/eth2/src/types.rs b/common/eth2/src/types.rs index 1186c318ab6..d86d311b0e1 100644 --- a/common/eth2/src/types.rs +++ b/common/eth2/src/types.rs @@ -835,7 +835,8 @@ impl Encode for LightClientUpdateResponseChunk { } fn ssz_bytes_len(&self) -> usize { - 0_u64.ssz_bytes_len() + self.response_chunk.context.len() + 0_u64.ssz_bytes_len() + + self.response_chunk.context.len() + match &self.response_chunk.payload { LightClientUpdate::Altair(lc) => lc.ssz_bytes_len(), LightClientUpdate::Capella(lc) => lc.ssz_bytes_len(), From ce33753b77398bb127690fd114d6293e0a77277a Mon Sep 17 00:00:00 2001 From: Eitan Seri-Levi Date: Tue, 5 Aug 2025 14:30:08 -0700 Subject: [PATCH 3/4] remove print --- beacon_node/http_api/src/light_client.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/beacon_node/http_api/src/light_client.rs b/beacon_node/http_api/src/light_client.rs index 1b97191bdc1..8fd47c52f29 100644 --- a/beacon_node/http_api/src/light_client.rs +++ b/beacon_node/http_api/src/light_client.rs @@ -164,8 +164,6 @@ fn map_light_client_update_to_response_chunk( payload: light_client_update, }; - println!("response chunk len {:?}", response_chunk_len); - LightClientUpdateResponseChunk { response_chunk_len: response_chunk_len as u64, response_chunk, From 67b45a9ea6b15d710e028a184879b1fbf96dbbe6 Mon Sep 17 00:00:00 2001 From: Eitan Seri-Levi Date: Tue, 12 Aug 2025 17:42:28 -0700 Subject: [PATCH 4/4] clean up --- common/eth2/src/lib.rs | 2 +- common/eth2/src/types.rs | 16 ++-------------- 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/common/eth2/src/lib.rs b/common/eth2/src/lib.rs index 6b0c6824fd8..bf0694dcedd 100644 --- a/common/eth2/src/lib.rs +++ b/common/eth2/src/lib.rs @@ -1003,7 +1003,7 @@ impl BeaconNodeHttpClient { path.query_pairs_mut() .append_pair("count", &count.to_string()); - self.get_bytes_opt_accept_header(path, Accept::Ssz, self.timeouts.get_beacon_blocks_ssz) + self.get_bytes_opt_accept_header(path, Accept::Ssz, self.timeouts.default) .await } diff --git a/common/eth2/src/types.rs b/common/eth2/src/types.rs index d86d311b0e1..0a60d0bc73a 100644 --- a/common/eth2/src/types.rs +++ b/common/eth2/src/types.rs @@ -837,25 +837,13 @@ impl Encode for LightClientUpdateResponseChunk { fn ssz_bytes_len(&self) -> usize { 0_u64.ssz_bytes_len() + self.response_chunk.context.len() - + match &self.response_chunk.payload { - LightClientUpdate::Altair(lc) => lc.ssz_bytes_len(), - LightClientUpdate::Capella(lc) => lc.ssz_bytes_len(), - LightClientUpdate::Deneb(lc) => lc.ssz_bytes_len(), - LightClientUpdate::Electra(lc) => lc.ssz_bytes_len(), - LightClientUpdate::Fulu(lc) => lc.ssz_bytes_len(), - } + + self.response_chunk.payload.ssz_bytes_len() } fn ssz_append(&self, buf: &mut Vec) { buf.extend_from_slice(&self.response_chunk_len.to_le_bytes()); buf.extend_from_slice(&self.response_chunk.context); - match &self.response_chunk.payload { - LightClientUpdate::Altair(lc) => lc.ssz_append(buf), - LightClientUpdate::Capella(lc) => lc.ssz_append(buf), - LightClientUpdate::Deneb(lc) => lc.ssz_append(buf), - LightClientUpdate::Electra(lc) => lc.ssz_append(buf), - LightClientUpdate::Fulu(lc) => lc.ssz_append(buf), - }; + self.response_chunk.payload.ssz_append(buf); } }