Skip to content

Commit 5b209ed

Browse files
committed
fix: change BlocksByRoot container de/serialization (#63)
The request type was changed in leanEthereum/leanSpec@054b2e9. This PR updates it to the latest format, while keeping backwards compatibility with the other one. This is because Zeam uses the new format, while the rest of clients use the old one. Once everyone moves to the new format we can remove this patch.
1 parent 33336e6 commit 5b209ed

5 files changed

Lines changed: 70 additions & 10 deletions

File tree

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ help: ## 📚 Show help for each of the Makefile recipes
66
lint: ## 🔍 Run clippy on all workspace crates
77
cargo clippy --workspace --all-targets -- -D warnings
88

9-
test: ## 🧪 Run all tests, then forkchoice tests with skip-signature-verification
9+
test: leanSpec/fixtures ## 🧪 Run all tests, then forkchoice tests with skip-signature-verification
1010
# Tests need to be run on release to avoid stack overflows during signature verification/aggregation
1111
cargo test --workspace --release
1212
cargo test -p ethlambda-blockchain --features skip-signature-verification --test forkchoice_spectests

crates/net/p2p/src/req_resp/codec.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,10 @@ impl libp2p::request_response::Codec for Codec {
4141
Ok(Request::Status(status))
4242
}
4343
BLOCKS_BY_ROOT_PROTOCOL_V1 => {
44-
let request = BlocksByRootRequest::from_ssz_bytes(&payload).map_err(|err| {
45-
io::Error::new(io::ErrorKind::InvalidData, format!("{err:?}"))
46-
})?;
44+
let request =
45+
BlocksByRootRequest::from_ssz_bytes_compat(&payload).map_err(|err| {
46+
io::Error::new(io::ErrorKind::InvalidData, format!("{err:?}"))
47+
})?;
4748
Ok(Request::BlocksByRoot(request))
4849
}
4950
_ => Err(io::Error::new(

crates/net/p2p/src/req_resp/handlers.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use super::{
1313
};
1414
use crate::{
1515
BACKOFF_MULTIPLIER, INITIAL_BACKOFF_MS, MAX_FETCH_RETRIES, P2PServer, PendingRequest,
16-
RetryMessage,
16+
RetryMessage, req_resp::RequestedBlockRoots,
1717
};
1818

1919
pub async fn handle_req_resp_message(
@@ -105,7 +105,7 @@ async fn handle_blocks_by_root_request(
105105
channel: request_response::ResponseChannel<Response>,
106106
peer: PeerId,
107107
) {
108-
let num_roots = request.len();
108+
let num_roots = request.roots.len();
109109
info!(%peer, num_roots, "Received BlocksByRoot request");
110110

111111
// TODO: Support multiple blocks per request (currently only handles first root)
@@ -198,11 +198,12 @@ pub async fn fetch_block_from_peer(
198198
};
199199

200200
// Create BlocksByRoot request with single root
201-
let mut request = BlocksByRootRequest::empty();
202-
if let Err(err) = request.push(root) {
201+
let mut roots = RequestedBlockRoots::empty();
202+
if let Err(err) = roots.push(root) {
203203
error!(%root, ?err, "Failed to create BlocksByRoot request");
204204
return false;
205205
}
206+
let request = BlocksByRootRequest { roots };
206207

207208
info!(%peer, %root, "Sending BlocksByRoot request for missing block");
208209
let request_id = server

crates/net/p2p/src/req_resp/messages.rs

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use ethlambda_types::{block::SignedBlockWithAttestation, primitives::H256, state::Checkpoint};
2+
use ssz::Decode as SszDecode;
23
use ssz_derive::{Decode, Encode};
34
use ssz_types::typenum;
45

@@ -102,7 +103,7 @@ pub struct Status {
102103
type MaxRequestBlocks = typenum::U1024;
103104
type MaxErrorMessageLength = typenum::U256;
104105

105-
pub type BlocksByRootRequest = ssz_types::VariableList<H256, MaxRequestBlocks>;
106+
pub type RequestedBlockRoots = ssz_types::VariableList<H256, MaxRequestBlocks>;
106107

107108
/// Error message type for non-success responses.
108109
/// SSZ-encoded as List[byte, 256] per spec.
@@ -121,3 +122,60 @@ pub fn error_message(msg: impl AsRef<str>) -> ErrorMessage {
121122
let vec = truncated.to_vec();
122123
ErrorMessage::new(vec).expect("error message fits in 256 bytes")
123124
}
125+
126+
#[derive(Debug, Clone, Encode, Decode)]
127+
pub struct BlocksByRootRequest {
128+
pub roots: RequestedBlockRoots,
129+
}
130+
131+
impl BlocksByRootRequest {
132+
/// Decode from SSZ bytes with backward compatibility.
133+
///
134+
/// Tries to decode as new format (container with `roots` field) first.
135+
/// Falls back to old format (transparent - direct list of roots) if that fails.
136+
pub fn from_ssz_bytes_compat(bytes: &[u8]) -> Result<Self, ssz::DecodeError> {
137+
// Try new format (container) first
138+
SszDecode::from_ssz_bytes(bytes).or_else(|_| {
139+
// Fall back to old format (transparent/direct list)
140+
SszDecode::from_ssz_bytes(bytes).map(|roots| Self { roots })
141+
})
142+
}
143+
}
144+
145+
#[cfg(test)]
146+
mod tests {
147+
use super::*;
148+
use ssz::Encode as SszEncode;
149+
150+
#[test]
151+
fn test_blocks_by_root_backward_compatibility() {
152+
// Create some test roots
153+
let root1 = H256::from_slice(&[1u8; 32]);
154+
let root2 = H256::from_slice(&[2u8; 32]);
155+
let roots_list =
156+
RequestedBlockRoots::new(vec![root1, root2]).expect("Failed to create roots list");
157+
158+
// Encode as old format (direct list, similar to transparent)
159+
let old_format_bytes = roots_list.as_ssz_bytes();
160+
161+
// Encode as new format (container)
162+
let new_request = BlocksByRootRequest {
163+
roots: roots_list.clone(),
164+
};
165+
let new_format_bytes = new_request.as_ssz_bytes();
166+
167+
// Both formats should decode successfully
168+
let decoded_from_old = BlocksByRootRequest::from_ssz_bytes_compat(&old_format_bytes)
169+
.expect("Failed to decode old format");
170+
let decoded_from_new = BlocksByRootRequest::from_ssz_bytes_compat(&new_format_bytes)
171+
.expect("Failed to decode new format");
172+
173+
// Both should have the same roots
174+
assert_eq!(decoded_from_old.roots.len(), 2);
175+
assert_eq!(decoded_from_new.roots.len(), 2);
176+
assert_eq!(decoded_from_old.roots[0], root1);
177+
assert_eq!(decoded_from_old.roots[1], root2);
178+
assert_eq!(decoded_from_new.roots[0], root1);
179+
assert_eq!(decoded_from_new.roots[1], root2);
180+
}
181+
}

crates/net/p2p/src/req_resp/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,6 @@ pub use codec::Codec;
77
pub use encoding::MAX_COMPRESSED_PAYLOAD_SIZE;
88
pub use handlers::{build_status, fetch_block_from_peer, handle_req_resp_message};
99
pub use messages::{
10-
BLOCKS_BY_ROOT_PROTOCOL_V1, BlocksByRootRequest, Request, Response, ResponseCode,
10+
BLOCKS_BY_ROOT_PROTOCOL_V1, BlocksByRootRequest, Request, RequestedBlockRoots, Response, ResponseCode,
1111
ResponsePayload, STATUS_PROTOCOL_V1, Status, error_message,
1212
};

0 commit comments

Comments
 (0)