Skip to content

The Serialize/Deserialize macro does not preserve all data. #2043

@lucasbalieiro

Description

@lucasbalieiro

while working on the improvements proposed in #2028, we noticed that the round-trip serialization is not lossless: deserializing a message and serializing it again can change the original byte stream.

To demonstrate the issue, you can apply this unit test for the NewExtendedMiningJob message that explicitly checks for byte-for-byte round-trip correctness.

Reproducer

The following test parses a raw byte vector into a NewExtendedMiningJob, serializes it back, and asserts that the output matches the original input exactly:

diff --git a/sv2/subprotocols/mining/src/new_mining_job.rs b/sv2/subprotocols/mining/src/new_mining_job.rs
index 9167be97..bf42800d 100644
--- a/sv2/subprotocols/mining/src/new_mining_job.rs
+++ b/sv2/subprotocols/mining/src/new_mining_job.rs
@@ -152,6 +152,8 @@ impl NewExtendedMiningJob<'_> {
 
 #[cfg(test)]
 mod tests {
+    use binary_sv2::GetSize;
+
     use super::*;
     use crate::tests::from_arbitrary_vec_to_array;
     use core::convert::TryFrom;
@@ -217,6 +219,24 @@ mod tests {
             && static_nmj.merkle_root == nmj.merkle_root
     }
 
+    #[test]
+    fn test_lossy_bytes() {
+        let mut bytes = vec![
+            255, 231, 255, 0, 50, 197, 5, 5, 1, 255, 255, 5, 5, 255, 5, 5, 1, 255, 0, 7, 0, 255,
+            255, 255, 255, 173, 0, 197, 0, 0,
+        ];
+
+        let msg = NewExtendedMiningJob::from_bytes(&mut bytes).unwrap();
+
+        let mut encoded = vec![0u8; msg.get_size()];
+        msg.to_bytes(&mut encoded).unwrap();
+
+        assert_eq!(
+            bytes, encoded,
+            "Expected a lossless byte round-trip, but encoding changed the input bytes"
+        );
+    }
+
     pub mod helpers {
         use super::*;
         use alloc::borrow::ToOwned;

Failure

Running the test:

cargo test test_lossy_bytes

results in the following failure:

running 1 test
test new_mining_job::tests::test_lossy_bytes ... FAILED

failures:

---- new_mining_job::tests::test_lossy_bytes stdout ----
thread 'new_mining_job::tests::test_lossy_bytes' panicked at sv2/subprotocols/mining/src/new_mining_job.rs:234:9:
assertion `left == right` failed: Expected a lossless byte round-trip, but encoding changed the input bytes
  left: [255, 231, 255, 0, 50, 197, 5, 5, 1, 255, 255, 5, 5, 255, 5, 5, 1, 255, 0, 7, 0, 255, 255, 255, 255, 173, 0, 197, 0, 0]
 right: [255, 231, 255, 0, 50, 197, 5, 5, 1, 255, 255, 5, 5, 255, 5, 5, 1, 1, 0, 7, 0, 255, 255, 255, 255, 173, 0, 197, 0, 0]
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    new_mining_job::tests::test_lossy_bytes

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 26 filtered out; finished in 0.00s

error: test failed, to rerun pass `--lib`

Specifically, the byte at index 17 is modified during the round-trip: its value changes from 255 to 1.

Image

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions