Repository navigation
tests: make Event::Encode match the JIP-3 wire format - #43
Conversation
LEB128 diverges from what real nodes (jam-codec) emit for values >= 128.
s0me0ne-unkn0wn
left a comment
There was a problem hiding this comment.
Looks good, left some minor comments.
| Event::WorkPackageSubmission { .. } => 32 + 1, // builder + bundle | ||
| Event::WorkPackageBeingShared { .. } => 32, // primary | ||
| Event::DuplicateWorkPackage { .. } => 8 + 2 + 32, // submission_or_share_id + core + hash | ||
| Event::ExtrinsicDataReceived { .. } => 8, // submission_or_share_id | ||
| Event::ImportsReceived { .. } => 8, // submission_or_share_id | ||
| Event::SharingWorkPackage { .. } => 8 + 32, // submission_id + secondary | ||
| Event::WorkPackageSharingFailed { reason, .. } => 8 + 32 + reason.encoded_size(), | ||
| Event::BundleSent { .. } => 8 + 32, // submission_id + secondary | ||
| Event::WorkReportSignatureSent { .. } => 8, // share_id | ||
| Event::WorkReportSignatureReceived { .. } => 8 + 32, // submission_id + secondary |
There was a problem hiding this comment.
We're just... Hardcoding them all? For real? That looks brittle 🤔
I'd discuss a refactoring of this entire module (out of the scope of this PR, obviously)
There was a problem hiding this comment.
follow up sounds good.
| self.anchor.encode(buf)?; | ||
| self.bitfield.encode(buf)?; // Encoded with variable-length prefix | ||
| // JIP-3: bitfield is [u8; ceil(core_count/8)] — fixed size, no length prefix | ||
| buf.extend_from_slice(&self.bitfield); |
There was a problem hiding this comment.
The fact that the encoding is a transparent pass-through does not generally mean we can omit the encoding call itself. Properly implemented, bitfield.encode() should be an identity call, and it should be performed here.
| #[test] | ||
| fn test_distributing_assurance_bitfield_fixed_no_prefix() { | ||
| // JIP-3: AvailabilityStatement bitfield is [u8; ceil(core_count/8)], fixed-size with NO | ||
| // length prefix. The decoder reads exactly ceil(core_count/8) bytes after the anchor. | ||
| use tart_backend::events::Event; | ||
| use tart_backend::types::AvailabilityStatement; | ||
|
|
||
| let core_count = 16u16; // ceil(16/8) = 2 bytes | ||
| let anchor = [0xABu8; 32]; | ||
| let bitfield = vec![0xFFu8; 2]; | ||
| let event = Event::DistributingAssurance { | ||
| timestamp: 1_700_000_000_000_000, | ||
| statement: AvailabilityStatement { anchor, bitfield }, | ||
| }; | ||
|
|
||
| let mut buf = BytesMut::new(); | ||
| event.encode(&mut buf).unwrap(); | ||
|
|
||
| // content = timestamp(8) + discriminator(1) + anchor(32) + bitfield(2) = 43 | ||
| assert_eq!(buf.len(), 43, "no length prefix expected for fixed bitfield"); | ||
| assert_eq!(buf[41], 0xFF); | ||
| assert_eq!(buf[42], 0xFF); | ||
|
|
||
| // Decoder must consume the bitfield without a prefix and round-trip. | ||
| let mut cursor = Cursor::new(&buf[..]); | ||
| let decoded = Event::decode_event(&mut cursor, core_count).unwrap(); | ||
| match decoded { | ||
| Event::DistributingAssurance { statement, .. } => { | ||
| assert_eq!(statement.anchor, anchor); | ||
| assert_eq!(statement.bitfield, vec![0xFFu8; 2]); | ||
| } | ||
| _ => panic!("wrong variant decoded"), | ||
| } | ||
| assert_eq!(cursor.position() as usize, buf.len(), "whole event consumed"); | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_sending_segment_shard_request_varlen_prefix() { | ||
| // BoundedVec<(u16,u16)> shards use a variable-length count prefix. | ||
| use tart_backend::events::Event; | ||
|
|
||
| let event = Event::SendingSegmentShardRequest { | ||
| timestamp: 1_700_000_000_000_000, | ||
| submission_id: 42, | ||
| assurer: [0x11u8; 32], | ||
| proofs: true, // bool — 1 byte, no prefix | ||
| shards: vec![(1u16, 2u16)], // BoundedVec<(u16,u16)> — varlen count 1 byte + 4 bytes | ||
| }; | ||
|
|
||
| let mut buf = BytesMut::new(); | ||
| event.encode(&mut buf).unwrap(); | ||
| assert_eq!(buf.len(), event.encoded_size()); | ||
|
|
||
| // Round-trip through the decoder. | ||
| let mut cursor = Cursor::new(&buf[..]); | ||
| let decoded = Event::decode_event(&mut cursor, 16).unwrap(); | ||
| assert_eq!(decoded.event_type() as u8, 162); // SendingSegmentShardRequest | ||
| let mut reencoded = BytesMut::new(); | ||
| decoded.encode(&mut reencoded).unwrap(); | ||
| assert_eq!(reencoded, buf); | ||
| } |
There was a problem hiding this comment.
Do we need tests for those narrow cases? I mean, we don't have a separate test for every event type, how are these ones different? (I'm not opposed to adding them, just thinking aloud).
There was a problem hiding this comment.
The truth is that whole this testsuit needs more love one day. I am in progress of reviewing it, sadly delayed by work in parachain-service, so improvements will come piece by piece.
The test harness pretends to be a JAM node, but its event encoder didn't match the real JIP-3 wire format (wrong bitfield and length encodings, and 10 event types just panicked). This fixes the encoder so tests send real protocol bytes, and adds tests that check the byte layout against the spec.