Skip to content

Commit 116eea9

Browse files
fix(iroh-bytes): Hash should be serialized as array not bytes (#1410)
1 parent 6d9eac7 commit 116eea9

3 files changed

Lines changed: 66 additions & 7 deletions

File tree

Cargo.lock

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

iroh-bytes/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ tracing-futures = "0.2.5"
4343
http-body = "0.4.5"
4444
iroh-test = { path = "../iroh-test" }
4545
proptest = "1.0.0"
46+
serde_test = "1.0.176"
4647
tokio = { version = "1", features = ["macros", "test-util"] }
4748

4849
[features]

iroh-bytes/src/util.rs

Lines changed: 55 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@
22
use anyhow::Result;
33
use bao_tree::blake3;
44
use postcard::experimental::max_size::MaxSize;
5-
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
5+
use serde::{
6+
de::{self, SeqAccess},
7+
ser::SerializeTuple,
8+
Deserialize, Deserializer, Serialize, Serializer,
9+
};
610
use std::{fmt, result, str::FromStr};
711
use thiserror::Error;
812
pub mod io;
@@ -156,7 +160,13 @@ impl Serialize for Hash {
156160
where
157161
S: Serializer,
158162
{
159-
serializer.serialize_bytes(self.0.as_bytes())
163+
// Fixed-length structures, including arrays, are supported in Serde as tuples
164+
// See: https://serde.rs/impl-serialize.html#serializing-a-tuple
165+
let mut s = serializer.serialize_tuple(32)?;
166+
for item in self.0.as_bytes() {
167+
s.serialize_element(item)?;
168+
}
169+
s.end()
160170
}
161171
}
162172

@@ -165,7 +175,7 @@ impl<'de> Deserialize<'de> for Hash {
165175
where
166176
D: Deserializer<'de>,
167177
{
168-
deserializer.deserialize_bytes(HashVisitor)
178+
deserializer.deserialize_tuple(32, HashVisitor)
169179
}
170180
}
171181

@@ -178,12 +188,22 @@ impl<'de> de::Visitor<'de> for HashVisitor {
178188
write!(f, "an array of 32 bytes containing hash data")
179189
}
180190

181-
fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
191+
/// Process a sequence into an array
192+
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
182193
where
183-
E: de::Error,
194+
A: SeqAccess<'de>,
184195
{
185-
let bytes: [u8; 32] = v.try_into().map_err(E::custom)?;
186-
Ok(Hash::from(bytes))
196+
let mut arr = [0u8; 32];
197+
let mut i = 0;
198+
while let Some(val) = seq.next_element()? {
199+
arr[i] = val;
200+
i += 1;
201+
if i > 32 {
202+
return Err(de::Error::invalid_length(i, &self));
203+
}
204+
}
205+
206+
Ok(Hash::from(arr))
187207
}
188208
}
189209

@@ -244,6 +264,8 @@ impl NonSend {
244264
mod tests {
245265
use super::*;
246266

267+
use serde_test::{assert_tokens, Token};
268+
247269
#[test]
248270
fn test_hash() {
249271
let data = b"hello world";
@@ -252,4 +274,30 @@ mod tests {
252274
let encoded = hash.to_string();
253275
assert_eq!(encoded.parse::<Hash>().unwrap(), hash);
254276
}
277+
278+
#[test]
279+
fn test_hash_serde() {
280+
let hash = Hash::new("hello");
281+
282+
// Hashes are serialized as 32 tuples
283+
let mut tokens = Vec::new();
284+
tokens.push(Token::Tuple { len: 32 });
285+
for byte in hash.as_bytes() {
286+
tokens.push(Token::U8(*byte));
287+
}
288+
tokens.push(Token::TupleEnd);
289+
assert_eq!(tokens.len(), 34);
290+
291+
assert_tokens(&hash, &tokens);
292+
}
293+
294+
#[test]
295+
fn test_hash_postcard() {
296+
let hash = Hash::new("hello");
297+
let ser = postcard::to_stdvec(&hash).unwrap();
298+
let de = postcard::from_bytes(&ser).unwrap();
299+
assert_eq!(hash, de);
300+
301+
assert_eq!(ser.len(), 32);
302+
}
255303
}

0 commit comments

Comments
 (0)