Skip to content

Commit 1fc6dbd

Browse files
authored
Merge pull request #8 from paritytech/fix/jip3-decoder-alignment
Fix/jip3 decoder alignment
2 parents cc275c3 + 0e814b6 commit 1fc6dbd

File tree

9 files changed

+79
-28
lines changed

9 files changed

+79
-28
lines changed

src/decoder.rs

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -589,12 +589,13 @@ impl Decode for Vec<ValidatorIndex> {
589589
}
590590
}
591591

592-
impl Decode for Event {
593-
fn decode(buf: &mut Cursor<&[u8]>) -> Result<Self, DecodingError> {
592+
impl Event {
593+
/// Decode an event from the wire format, using `core_count` (C) from
594+
/// the node's ProtocolParameters for fixed-size [u8; C] arrays (JIP-3).
595+
pub fn decode_event(buf: &mut Cursor<&[u8]>, core_count: u16) -> Result<Self, DecodingError> {
594596
let timestamp = Timestamp::decode(buf)?;
595597
let discriminator = u8::decode(buf)?;
596598

597-
// Debug logging
598599
if ![10, 11, 12, 13].contains(&discriminator) {
599600
tracing::trace!(
600601
"Decoding event with discriminator: {}, remaining bytes: {}",
@@ -614,14 +615,12 @@ impl Decode for Event {
614615
let num_val_peers = u32::decode(buf)?;
615616
let num_sync_peers = u32::decode(buf)?;
616617

617-
// TODO: JIP-3 specifies num_guarantees as [u8; C] (fixed-size array where
618-
// C = core_count), but PolkaJam sends a varint length prefix before the
619-
// byte array. We decode it as variable-length to match actual wire format.
620-
let core_count = decode_variable_length(buf)? as usize;
621-
let mut num_guarantees = vec![0u8; core_count];
622-
if buf.remaining() < core_count {
618+
// JIP-3: num_guarantees is [u8; C] — fixed-size array, no length prefix
619+
let c = core_count as usize;
620+
let mut num_guarantees = vec![0u8; c];
621+
if buf.remaining() < c {
623622
return Err(DecodingError::InsufficientData {
624-
needed: core_count,
623+
needed: c,
625624
available: buf.remaining(),
626625
});
627626
}
@@ -982,10 +981,24 @@ impl Decode for Event {
982981
}),
983982

984983
// Assurance distribution (126-131)
985-
126 => Ok(Event::DistributingAssurance {
986-
timestamp,
987-
statement: AvailabilityStatement::decode(buf)?,
988-
}),
984+
126 => {
985+
// JIP-3: AvailabilityStatement is anchor (32 bytes) +
986+
// bitfield [u8; ceil(C/8)] — fixed-size, no length prefix
987+
let anchor = HeaderHash::decode(buf)?;
988+
let bitfield_len = (core_count as usize).div_ceil(8);
989+
if buf.remaining() < bitfield_len {
990+
return Err(DecodingError::InsufficientData {
991+
needed: bitfield_len,
992+
available: buf.remaining(),
993+
});
994+
}
995+
let mut bitfield = vec![0u8; bitfield_len];
996+
buf.copy_to_slice(&mut bitfield);
997+
Ok(Event::DistributingAssurance {
998+
timestamp,
999+
statement: AvailabilityStatement { anchor, bitfield },
1000+
})
1001+
}
9891002
127 => Ok(Event::AssuranceSendFailed {
9901003
timestamp,
9911004
distributing_id: EventId::decode(buf)?,
@@ -1245,6 +1258,15 @@ impl Decode for Event {
12451258
}
12461259
}
12471260

1261+
impl Decode for Event {
1262+
fn decode(buf: &mut Cursor<&[u8]>) -> Result<Self, DecodingError> {
1263+
// Fallback: decode with core_count=0 (Status/DistributingAssurance will
1264+
// produce empty arrays). Use decode_event() directly with the real
1265+
// core_count for production decoding.
1266+
Event::decode_event(buf, 0)
1267+
}
1268+
}
1269+
12481270
pub fn decode_message_frame(data: &[u8]) -> Result<(u32, &[u8]), DecodingError> {
12491271
if data.len() < 4 {
12501272
return Err(DecodingError::InsufficientData {

src/events.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1203,7 +1203,8 @@ impl Encode for Event {
12031203
num_peers.encode(buf)?;
12041204
num_val_peers.encode(buf)?;
12051205
num_sync_peers.encode(buf)?;
1206-
num_guarantees.encode(buf)?;
1206+
// JIP-3: num_guarantees is [u8; C] — raw bytes, no length prefix
1207+
buf.extend_from_slice(num_guarantees);
12071208
num_shards.encode(buf)?;
12081209
shards_size.encode(buf)?;
12091210
num_preimages.encode(buf)?;
@@ -1487,7 +1488,8 @@ impl Encode for Event {
14871488
let specific_size = match self {
14881489
Event::Dropped { .. } => 8 + 8, // last_timestamp + num (u64)
14891490
Event::Status { num_guarantees, .. } => {
1490-
4 + 4 + 4 + num_guarantees.encoded_size() + 4 + 8 + 4 + 4
1491+
// JIP-3: num_guarantees is [u8; C] — raw bytes, no length prefix
1492+
4 + 4 + 4 + num_guarantees.len() + 4 + 8 + 4 + 4
14911493
}
14921494
Event::BestBlockChanged { .. } => 4 + 32, // slot + hash
14931495
Event::FinalizedBlockChanged { .. } => 4 + 32,

src/server.rs

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,9 @@ async fn handle_connection_optimized(
375375
}
376376
};
377377

378+
// Extract core_count from ProtocolParameters for JIP-3 fixed-size arrays
379+
let core_count = node_info.params.core_count;
380+
378381
// Generate node ID from peer ID
379382
let node_id_str = hex::encode(node_info.details.peer_id);
380383

@@ -431,7 +434,7 @@ async fn handle_connection_optimized(
431434
}
432435

433436
let mut cursor = Cursor::new(msg_data);
434-
match Event::decode(&mut cursor) {
437+
match Event::decode_event(&mut cursor, core_count) {
435438
Ok(event) => {
436439
event_count += 1;
437440

@@ -500,7 +503,25 @@ async fn handle_connection_optimized(
500503
buffer.advance(4 + size as usize);
501504
}
502505
Err(e) => {
503-
warn!("Failed to decode event from {}: {}", node_id_str, e);
506+
let event_type_hint = if msg_data.len() > 8 {
507+
format!(" (event_type={})", msg_data[8])
508+
} else {
509+
String::new()
510+
};
511+
let hex_preview: String = msg_data
512+
.iter()
513+
.take(32)
514+
.map(|b| format!("{:02x}", b))
515+
.collect::<Vec<_>>()
516+
.join(" ");
517+
warn!(
518+
"Failed to decode event from {}{}: {} [msg_len={}, hex={}]",
519+
node_id_str,
520+
event_type_hint,
521+
e,
522+
msg_data.len(),
523+
hex_preview
524+
);
504525
buffer.advance(4 + size as usize);
505526
}
506527
}

tests/api_tests.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ async fn test_events_endpoint_with_data() {
227227
num_val_peers: 5,
228228
num_peers: 10,
229229
num_sync_peers: 8,
230-
num_guarantees: vec![1, 2, 3, 4],
230+
num_guarantees: vec![0u8; common::TEST_CORE_COUNT],
231231
num_shards: 50,
232232
shards_size: 1024 * 1024,
233233
num_preimages: 3,
@@ -1184,7 +1184,7 @@ async fn test_core_guarantees_with_status_data() {
11841184
num_val_peers: 10,
11851185
num_peers: 20,
11861186
num_sync_peers: 15,
1187-
num_guarantees: vec![3, 1, 4, 1, 5, 9, 2, 6],
1187+
num_guarantees: vec![0u8; common::TEST_CORE_COUNT],
11881188
num_shards: 100,
11891189
shards_size: 1024 * 1024,
11901190
num_preimages: 5,
@@ -1217,7 +1217,7 @@ async fn test_da_stats_with_status_data() {
12171217
num_val_peers: 10,
12181218
num_peers: 20,
12191219
num_sync_peers: 15,
1220-
num_guarantees: vec![1, 2, 3],
1220+
num_guarantees: vec![0u8; common::TEST_CORE_COUNT],
12211221
num_shards: 150,
12221222
shards_size: 2 * 1024 * 1024,
12231223
num_preimages: 8,

tests/common/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ use tart_backend::types::*;
55
use tart_backend::TelemetryServer;
66
use tokio::time::sleep;
77

8+
/// Core count used in test_protocol_params(). Status events must have
9+
/// num_guarantees with exactly this many elements to match the decoder.
10+
#[allow(dead_code)]
11+
pub const TEST_CORE_COUNT: usize = 16;
12+
813
/// Returns a "now" timestamp in JCE-relative microseconds.
914
/// Use this in test events so they fall within time-bounded query windows.
1015
#[allow(dead_code)]

tests/error_tests.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -382,7 +382,7 @@ fn test_event_edge_cases() {
382382
event.encode(&mut buf).unwrap();
383383

384384
let mut cursor = Cursor::new(&buf[..]);
385-
let decoded = Event::decode(&mut cursor).unwrap();
385+
let decoded = Event::decode_event(&mut cursor, 1000).unwrap();
386386

387387
match decoded {
388388
Event::Status { num_guarantees, .. } => {

tests/events_tests.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,9 @@ fn test_status_event_encoding_decoding() {
9090
// Check discriminator
9191
assert_eq!(buf[8], 10);
9292

93+
let core_count = guarantees.len() as u16;
9394
let mut cursor = Cursor::new(&buf[..]);
94-
let decoded = Event::decode(&mut cursor).unwrap();
95+
let decoded = Event::decode_event(&mut cursor, core_count).unwrap();
9596

9697
match decoded {
9798
Event::Status {
@@ -717,9 +718,9 @@ fn test_status_event_with_message_frame() {
717718
let (size, msg_data) = decode_message_frame(&encoded).unwrap();
718719
println!("Frame size: {}, data len: {}", size, msg_data.len());
719720

720-
// Decode the event
721+
// Decode the event (core_count = 4 to match num_guarantees length)
721722
let mut cursor = Cursor::new(msg_data);
722-
let decoded = Event::decode(&mut cursor).unwrap();
723+
let decoded = Event::decode_event(&mut cursor, 4).unwrap();
723724

724725
match decoded {
725726
Event::Status {

tests/integration_tests.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ async fn test_send_multiple_events() {
8585
num_val_peers: 5,
8686
num_peers: 10,
8787
num_sync_peers: 8,
88-
num_guarantees: vec![0, 1, 2, 3],
88+
num_guarantees: vec![0u8; common::TEST_CORE_COUNT],
8989
num_shards: 50,
9090
shards_size: 1024 * 1024,
9191
num_preimages: 3,
@@ -146,7 +146,7 @@ async fn test_multiple_concurrent_connections() {
146146
num_val_peers: i as u32,
147147
num_peers: j,
148148
num_sync_peers: 0,
149-
num_guarantees: vec![],
149+
num_guarantees: vec![0u8; common::TEST_CORE_COUNT],
150150
num_shards: 0,
151151
shards_size: 0,
152152
num_preimages: 0,

tests/optimized_server_tests.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ async fn test_optimized_server_handles_multiple_connections() {
7272
num_val_peers: 10,
7373
num_peers: 5,
7474
num_sync_peers: 15,
75-
num_guarantees: vec![1, 2, 3],
75+
num_guarantees: vec![0u8; common::TEST_CORE_COUNT],
7676
num_shards: 3,
7777
shards_size: 1024,
7878
num_preimages: 0,

0 commit comments

Comments
 (0)