Skip to main content

p2panda_core/
serde.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3use std::fmt;
4use std::marker::PhantomData;
5
6use serde::de::{Error as SerdeError, SeqAccess, Visitor};
7use serde::ser::SerializeSeq;
8use serde::{Deserialize, Serialize};
9use serde_bytes::{ByteBuf as SerdeByteBuf, Bytes as SerdeBytes};
10
11use crate::cursor::Cursor;
12use crate::hash::{Hash, HashError};
13use crate::identity::{Author, IdentityError, Signature, SigningKey, VerifyingKey};
14use crate::logs::{LogHeights, LogId, SeqNum};
15use crate::operation::{Body, Header, Version};
16use crate::topic::{Topic, TopicError};
17
18/// Helper method for `serde` to serialize bytes into a hex string when using a human readable
19/// encoding (JSON, GraphQL), otherwise it serializes the bytes directly (CBOR).
20pub fn serialize_hex<S>(value: &[u8], serializer: S) -> Result<S::Ok, S::Error>
21where
22    S: serde::Serializer,
23{
24    if serializer.is_human_readable() {
25        hex::serde::serialize(value, serializer)
26    } else {
27        SerdeBytes::new(value).serialize(serializer)
28    }
29}
30
31/// Helper method for `serde` to deserialize from a hex string into bytes when using a human
32/// readable encoding (JSON, GraphQL), otherwise it deserializes the bytes directly (CBOR).
33pub fn deserialize_hex<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
34where
35    D: serde::Deserializer<'de>,
36{
37    if deserializer.is_human_readable() {
38        hex::serde::deserialize(deserializer)
39    } else {
40        let bytes = <SerdeByteBuf>::deserialize(deserializer)?;
41        Ok(bytes.to_vec())
42    }
43}
44
45impl Serialize for Hash {
46    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
47    where
48        S: serde::Serializer,
49    {
50        serialize_hex(self.as_bytes(), serializer)
51    }
52}
53
54impl<'de> Deserialize<'de> for Hash {
55    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
56    where
57        D: serde::Deserializer<'de>,
58    {
59        let bytes = deserialize_hex(deserializer)?;
60
61        bytes
62            .as_slice()
63            .try_into()
64            .map_err(|err: HashError| serde::de::Error::custom(err.to_string()))
65    }
66}
67
68impl Serialize for SigningKey {
69    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
70    where
71        S: serde::Serializer,
72    {
73        serialize_hex(self.as_bytes(), serializer)
74    }
75}
76
77impl<'de> Deserialize<'de> for SigningKey {
78    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
79    where
80        D: serde::Deserializer<'de>,
81    {
82        let bytes = deserialize_hex(deserializer)?;
83
84        bytes
85            .as_slice()
86            .try_into()
87            .map_err(|err: IdentityError| serde::de::Error::custom(err.to_string()))
88    }
89}
90
91impl Serialize for VerifyingKey {
92    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
93    where
94        S: serde::Serializer,
95    {
96        serialize_hex(self.as_bytes(), serializer)
97    }
98}
99
100impl<'de> Deserialize<'de> for VerifyingKey {
101    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
102    where
103        D: serde::Deserializer<'de>,
104    {
105        let bytes = deserialize_hex(deserializer)?;
106
107        bytes
108            .as_slice()
109            .try_into()
110            .map_err(|err: IdentityError| serde::de::Error::custom(err.to_string()))
111    }
112}
113
114impl Serialize for Signature {
115    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
116    where
117        S: serde::Serializer,
118    {
119        serialize_hex(&self.to_bytes(), serializer)
120    }
121}
122
123impl<'de> Deserialize<'de> for Signature {
124    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
125    where
126        D: serde::Deserializer<'de>,
127    {
128        let bytes = deserialize_hex(deserializer)?;
129
130        bytes
131            .as_slice()
132            .try_into()
133            .map_err(|err: IdentityError| serde::de::Error::custom(err.to_string()))
134    }
135}
136
137impl<E> Serialize for Header<E>
138where
139    E: Serialize,
140{
141    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
142    where
143        S: serde::Serializer,
144    {
145        let mut seq = serializer.serialize_seq(Some(self.field_count()))?;
146        seq.serialize_element(&self.version)?;
147        seq.serialize_element(&self.verifying_key)?;
148
149        if let Some(signature) = &self.signature {
150            seq.serialize_element(signature)?;
151        }
152
153        seq.serialize_element(&self.payload_size)?;
154        if let Some(hash) = &self.payload_hash {
155            seq.serialize_element(&hash)?;
156        }
157
158        seq.serialize_element(&self.seq_num)?;
159
160        if let Some(backlink) = &self.backlink {
161            seq.serialize_element(backlink)?;
162        }
163
164        if Self::has_non_zero_sized_extensions() {
165            seq.serialize_element(&self.extensions)?;
166        }
167
168        seq.end()
169    }
170}
171
172impl<'de, E> Deserialize<'de> for Header<E>
173where
174    E: Deserialize<'de>,
175{
176    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
177    where
178        D: serde::Deserializer<'de>,
179    {
180        struct HeaderVisitor<E> {
181            _marker: PhantomData<E>,
182        }
183
184        impl<'de, E> Visitor<'de> for HeaderVisitor<E>
185        where
186            E: Deserialize<'de>,
187        {
188            type Value = Header<E>;
189
190            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
191                formatter.write_str("Header encoded as a sequence")
192            }
193
194            fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
195            where
196                A: SeqAccess<'de>,
197            {
198                let version: Version = seq
199                    .next_element()?
200                    .ok_or(SerdeError::custom("version missing"))?;
201
202                let verifying_key: VerifyingKey = seq
203                    .next_element()?
204                    .ok_or(SerdeError::custom("public key missing"))?;
205
206                let signature: Signature = seq
207                    .next_element()?
208                    .ok_or(SerdeError::custom("signature missing"))?;
209
210                let payload_size: u32 = seq
211                    .next_element()?
212                    .ok_or(SerdeError::custom("payload size missing"))?;
213
214                let payload_hash: Option<Hash> = match payload_size {
215                    0 => None,
216                    _ => {
217                        let hash: Hash = seq
218                            .next_element()?
219                            .ok_or(SerdeError::custom("payload hash missing"))?;
220                        Some(hash)
221                    }
222                };
223
224                let seq_num: SeqNum = seq
225                    .next_element()?
226                    .ok_or(SerdeError::custom("sequence number missing"))?;
227
228                let backlink: Option<Hash> = match seq_num {
229                    0 => None,
230                    _ => {
231                        let hash: Hash = seq
232                            .next_element()?
233                            .ok_or(SerdeError::custom("backlink missing"))?;
234                        Some(hash)
235                    }
236                };
237
238                let extensions: E = if Header::<E>::has_non_zero_sized_extensions() {
239                    seq.next_element()?
240                        .ok_or(SerdeError::custom("extensions missing"))?
241                } else {
242                    Header::<E>::zero_sized_extensions()
243                };
244
245                if let Some(remainder) = seq.size_hint()
246                    && remainder > 0
247                {
248                    return Err(SerdeError::custom(format!(
249                        "unexpected {remainder} excessive field(s) in header"
250                    )));
251                }
252
253                Ok(Header {
254                    version,
255                    verifying_key,
256                    signature: Some(signature),
257                    payload_hash,
258                    payload_size,
259                    seq_num,
260                    backlink,
261                    extensions,
262                })
263            }
264        }
265
266        deserializer.deserialize_seq(HeaderVisitor::<E> {
267            _marker: PhantomData,
268        })
269    }
270}
271
272impl Serialize for Body {
273    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
274    where
275        S: serde::Serializer,
276    {
277        serialize_hex(&self.0, serializer)
278    }
279}
280
281impl<'de> Deserialize<'de> for Body {
282    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
283    where
284        D: serde::Deserializer<'de>,
285    {
286        let bytes = deserialize_hex(deserializer)?;
287        Ok(Body(bytes.to_vec()))
288    }
289}
290
291impl Serialize for Topic {
292    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
293    where
294        S: serde::Serializer,
295    {
296        serialize_hex(&self.0, serializer)
297    }
298}
299
300impl<'de> Deserialize<'de> for Topic {
301    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
302    where
303        D: serde::Deserializer<'de>,
304    {
305        let bytes = deserialize_hex(deserializer)?;
306
307        bytes
308            .as_slice()
309            .try_into()
310            .map_err(|err: TopicError| serde::de::Error::custom(err.to_string()))
311    }
312}
313
314impl<A, L> Serialize for Cursor<A, L>
315where
316    A: Author,
317    L: LogId,
318{
319    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
320    where
321        S: serde::Serializer,
322    {
323        let mut seq = serializer.serialize_seq(Some(2))?;
324        seq.serialize_element(self.name())?;
325        seq.serialize_element(self.state())?;
326        seq.end()
327    }
328}
329
330impl<'de, A, L> Deserialize<'de> for Cursor<A, L>
331where
332    A: Author,
333    L: LogId,
334{
335    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
336    where
337        D: serde::Deserializer<'de>,
338    {
339        struct CursorVisitor<A, L> {
340            _marker: PhantomData<(A, L)>,
341        }
342
343        impl<'de, A, L> Visitor<'de> for CursorVisitor<A, L>
344        where
345            A: Author,
346            L: LogId,
347        {
348            type Value = Cursor<A, L>;
349
350            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
351                formatter.write_str("Cursor encoded as a sequence")
352            }
353
354            fn visit_seq<T>(self, mut seq: T) -> Result<Self::Value, T::Error>
355            where
356                T: SeqAccess<'de>,
357            {
358                let name: String = seq
359                    .next_element()?
360                    .ok_or(SerdeError::custom("cursor id missing"))?;
361
362                let state: LogHeights<A, L> = seq
363                    .next_element()?
364                    .ok_or(SerdeError::custom("state vector missing"))?;
365
366                Ok(Cursor::new(name, state))
367            }
368        }
369
370        deserializer.deserialize_seq(CursorVisitor::<A, L> {
371            _marker: PhantomData,
372        })
373    }
374}
375
376#[cfg(test)]
377mod tests {
378    use serde::de::DeserializeOwned;
379    use serde::{Deserialize, Serialize};
380
381    use crate::Body;
382    use crate::hash::Hash;
383    use crate::identity::{SigningKey, VerifyingKey};
384    use crate::operation::Header;
385
386    use super::{deserialize_hex, serialize_hex};
387
388    #[derive(Debug, Serialize, Deserialize)]
389    struct Test(
390        #[serde(serialize_with = "serialize_hex", deserialize_with = "deserialize_hex")] Vec<u8>,
391    );
392
393    #[test]
394    fn serialize() {
395        let mut bytes: Vec<u8> = Vec::new();
396        let test = Test(vec![1, 2, 3]);
397
398        // For CBOR the bytes just get serialized straight away as it is not a human readable
399        // encoding
400        ciborium::ser::into_writer(&test, &mut bytes).unwrap();
401        assert_eq!(vec![67, 1, 2, 3], bytes);
402    }
403
404    #[test]
405    fn deserialize() {
406        let bytes: Vec<u8> = vec![67, 1, 2, 3];
407
408        // For CBOR the bytes just get deserialized straight away as an array as it is not a human
409        // readable encoding
410        let test: Test = ciborium::de::from_reader(&bytes[..]).unwrap();
411        assert_eq!(test.0, vec![1, 2, 3]);
412    }
413
414    #[test]
415    fn serialize_hash() {
416        // Serialize CBOR (non human-readable byte encoding)
417        let mut bytes: Vec<u8> = Vec::new();
418        let hash = Hash::digest([1, 2, 3]);
419        ciborium::ser::into_writer(&hash, &mut bytes).unwrap();
420        assert_eq!(
421            bytes,
422            vec![
423                88, 32, 177, 119, 236, 27, 242, 109, 251, 59, 112, 16, 212, 115, 230, 212, 71, 19,
424                178, 155, 118, 91, 153, 198, 230, 14, 203, 250, 231, 66, 222, 73, 101, 67
425            ]
426        );
427
428        // Serialize JSON (human-readable hex encoding)
429        let json = serde_json::to_string(&hash).unwrap();
430        assert_eq!(
431            json,
432            "\"b177ec1bf26dfb3b7010d473e6d44713b29b765b99c6e60ecbfae742de496543\""
433        );
434    }
435
436    #[test]
437    fn deserialize_hash() {
438        // Deserialize CBOR (non human-readable byte encoding)
439        let bytes = [
440            88, 32, 177, 119, 236, 27, 242, 109, 251, 59, 112, 16, 212, 115, 230, 212, 71, 19, 178,
441            155, 118, 91, 153, 198, 230, 14, 203, 250, 231, 66, 222, 73, 101, 67,
442        ];
443        let hash: Hash = ciborium::de::from_reader(&bytes[..]).unwrap();
444        assert_eq!(hash, Hash::digest([1, 2, 3]));
445
446        // Deserialize JSON (human-readable hex encoding)
447        let json = "\"b177ec1bf26dfb3b7010d473e6d44713b29b765b99c6e60ecbfae742de496543\"";
448        let hash: Hash = serde_json::from_str(json).unwrap();
449        assert_eq!(hash, Hash::digest([1, 2, 3]));
450    }
451
452    #[test]
453    fn serialize_verifying_key() {
454        // Serialize CBOR (non human-readable byte encoding)
455        let mut bytes: Vec<u8> = Vec::new();
456        let verifying_key = VerifyingKey::from_bytes(&[
457            215, 90, 152, 1, 130, 177, 10, 183, 213, 75, 254, 211, 201, 100, 7, 58, 14, 225, 114,
458            243, 218, 166, 35, 37, 175, 2, 26, 104, 247, 7, 81, 26,
459        ])
460        .unwrap();
461        ciborium::ser::into_writer(&verifying_key, &mut bytes).unwrap();
462        assert_eq!(
463            bytes,
464            vec![
465                88, 32, 215, 90, 152, 1, 130, 177, 10, 183, 213, 75, 254, 211, 201, 100, 7, 58, 14,
466                225, 114, 243, 218, 166, 35, 37, 175, 2, 26, 104, 247, 7, 81, 26,
467            ]
468        );
469
470        // Serialize JSON (human-readable hex encoding)
471        let json = serde_json::to_string(&verifying_key).unwrap();
472        assert_eq!(
473            json,
474            "\"d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a\""
475        );
476    }
477
478    fn assert_serde_roundtrip<
479        E: Clone + std::fmt::Debug + PartialEq + Serialize + DeserializeOwned,
480    >(
481        mut header: Header<E>,
482        signing_key: &SigningKey,
483    ) {
484        header.sign(signing_key);
485
486        let mut bytes = Vec::new();
487        ciborium::ser::into_writer(&header, &mut bytes).unwrap();
488        let header_again: Header<E> = ciborium::de::from_reader(&bytes[..]).unwrap();
489        assert_eq!(header, header_again);
490    }
491
492    #[test]
493    fn serde_roundtrip_operations() {
494        #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
495        struct CustomExtensions {
496            custom_field: u64,
497        }
498
499        let extensions = CustomExtensions { custom_field: 12 };
500        let signing_key = SigningKey::generate();
501
502        assert_serde_roundtrip(
503            Header::<CustomExtensions> {
504                version: 1,
505                verifying_key: signing_key.verifying_key(),
506                payload_size: 123,
507                payload_hash: Some(Hash::digest(vec![1, 2, 3])),
508                seq_num: 0,
509                backlink: None,
510                extensions: extensions.clone(),
511                signature: None,
512            },
513            &signing_key,
514        );
515
516        assert_serde_roundtrip(
517            Header::<CustomExtensions> {
518                version: 1,
519                verifying_key: signing_key.verifying_key(),
520                payload_size: 0,
521                payload_hash: None,
522                seq_num: 0,
523                backlink: None,
524                extensions: extensions,
525                signature: None,
526            },
527            &signing_key,
528        );
529    }
530
531    #[test]
532    fn expected_de_error() {
533        let signing_key = SigningKey::generate();
534
535        // payload size given without payload hash
536        let mut header = Header::<()> {
537            version: 1,
538            verifying_key: signing_key.verifying_key(),
539            signature: None,
540            payload_size: 2829099,
541            payload_hash: None,
542            seq_num: 0,
543            backlink: None,
544            extensions: (),
545        };
546        header.sign(&signing_key);
547
548        let result = ciborium::de::from_reader::<Header<()>, _>(&header.to_bytes()[..]);
549        assert!(result.is_err());
550
551        // payload hash given without payload size
552        let mut header = Header::<()> {
553            version: 1,
554            verifying_key: signing_key.verifying_key(),
555            signature: None,
556            payload_size: 0,
557            payload_hash: Some(Hash::digest([0, 1, 2])),
558            seq_num: 0,
559            backlink: None,
560            extensions: (),
561        };
562        header.sign(&signing_key);
563
564        let result = ciborium::de::from_reader::<Header<()>, _>(&header.to_bytes()[..]);
565        assert!(result.is_err());
566
567        // backlink given with seq number 0
568        let mut header = Header::<()> {
569            version: 1,
570            verifying_key: signing_key.verifying_key(),
571            signature: None,
572            payload_size: 0,
573            payload_hash: None,
574            seq_num: 0,
575            backlink: Some(Hash::digest([0, 1, 2])),
576            extensions: (),
577        };
578        header.sign(&signing_key);
579
580        let result = ciborium::de::from_reader::<Header<()>, _>(&header.to_bytes()[..]);
581        assert!(result.is_err());
582
583        // backlink not given with seq number > 0
584        let mut header = Header::<()> {
585            version: 1,
586            verifying_key: signing_key.verifying_key(),
587            signature: None,
588            payload_size: 0,
589            payload_hash: None,
590            seq_num: 10,
591            backlink: None,
592            extensions: (),
593        };
594        header.sign(&signing_key);
595
596        let result = ciborium::de::from_reader::<Header<()>, _>(&header.to_bytes()[..]);
597        assert!(result.is_err());
598    }
599
600    #[test]
601    fn serde_header_with_other_types() {
602        let signing_key = SigningKey::generate();
603
604        #[derive(Debug, PartialEq, Serialize, Deserialize)]
605        struct Message {
606            header: Header<()>,
607            body: Body,
608        }
609
610        let body = Body::new(b"hello");
611        let mut header = Header::<()> {
612            version: 1,
613            verifying_key: signing_key.verifying_key(),
614            signature: None,
615            payload_size: body.size(),
616            payload_hash: Some(body.hash()),
617            seq_num: 0,
618            backlink: None,
619            extensions: (),
620        };
621        header.sign(&signing_key);
622
623        let message = Message { header, body };
624
625        let mut bytes = Vec::new();
626        ciborium::ser::into_writer(&message, &mut bytes).unwrap();
627
628        let message_again: Message = ciborium::de::from_reader(&bytes[..]).unwrap();
629        assert_eq!(message_again, message);
630    }
631
632    #[test]
633    fn fixtures() {
634        let signing_key = SigningKey::from_bytes(&[
635            244, 123, 85, 215, 161, 204, 94, 227, 239, 253, 128, 164, 228, 160, 195, 49, 18, 49,
636            125, 4, 50, 218, 157, 230, 174, 1, 154, 231, 231, 142, 22, 170,
637        ]);
638
639        // header at seq num 0
640        let mut header = Header::<()> {
641            version: 1,
642            verifying_key: signing_key.verifying_key(),
643            signature: None,
644            payload_size: 0,
645            payload_hash: None,
646            seq_num: 0,
647            backlink: None,
648            extensions: (),
649        };
650        header.sign(&signing_key);
651
652        let bytes = [
653            133, 1, 88, 32, 228, 21, 196, 25, 12, 199, 241, 100, 122, 89, 46, 191, 142, 95, 144,
654            92, 42, 222, 249, 148, 139, 23, 91, 43, 92, 17, 225, 69, 17, 181, 22, 32, 88, 64, 17,
655            129, 90, 32, 212, 224, 74, 141, 219, 82, 160, 35, 19, 205, 82, 55, 247, 204, 121, 153,
656            128, 203, 123, 102, 108, 90, 60, 23, 223, 176, 251, 154, 243, 131, 177, 54, 142, 210,
657            0, 231, 125, 90, 206, 28, 240, 37, 179, 88, 200, 246, 185, 49, 246, 135, 242, 133, 128,
658            127, 22, 118, 23, 102, 22, 2, 0, 0,
659        ];
660
661        let header_again: Header<()> = ciborium::de::from_reader(&bytes[..]).unwrap();
662        assert_eq!(header, header_again);
663
664        // header at seq num 0 with body
665        let body = Body::new("Hello, Sloth!".as_bytes());
666        let mut header = Header::<()> {
667            version: 1,
668            verifying_key: signing_key.verifying_key(),
669            signature: None,
670            payload_size: body.size(),
671            payload_hash: Some(body.hash()),
672            seq_num: 0,
673            backlink: None,
674            extensions: (),
675        };
676        header.sign(&signing_key);
677
678        let bytes = [
679            134, 1, 88, 32, 228, 21, 196, 25, 12, 199, 241, 100, 122, 89, 46, 191, 142, 95, 144,
680            92, 42, 222, 249, 148, 139, 23, 91, 43, 92, 17, 225, 69, 17, 181, 22, 32, 88, 64, 187,
681            89, 157, 165, 197, 22, 79, 145, 227, 116, 226, 203, 231, 213, 225, 253, 197, 253, 240,
682            147, 16, 224, 186, 146, 94, 126, 79, 185, 150, 84, 102, 16, 109, 56, 241, 228, 164,
683            191, 153, 47, 142, 189, 12, 71, 159, 143, 81, 204, 108, 124, 22, 39, 222, 122, 88, 198,
684            123, 125, 2, 211, 28, 196, 90, 0, 13, 88, 32, 191, 127, 68, 13, 227, 43, 252, 155, 49,
685            148, 176, 2, 162, 217, 175, 171, 49, 44, 181, 215, 71, 113, 211, 195, 29, 128, 192,
686            169, 5, 138, 160, 142, 0,
687        ];
688
689        let header_again: Header<()> = ciborium::de::from_reader(&bytes[..]).unwrap();
690        assert_eq!(header, header_again);
691
692        // header at seq num 1 with backlink
693        let mut header = Header::<()> {
694            version: 1,
695            verifying_key: signing_key.verifying_key(),
696            signature: None,
697            payload_size: 0,
698            payload_hash: None,
699            seq_num: 1,
700            backlink: Some(header.hash()),
701            extensions: (),
702        };
703        header.sign(&signing_key);
704
705        let bytes = [
706            134, 1, 88, 32, 228, 21, 196, 25, 12, 199, 241, 100, 122, 89, 46, 191, 142, 95, 144,
707            92, 42, 222, 249, 148, 139, 23, 91, 43, 92, 17, 225, 69, 17, 181, 22, 32, 88, 64, 90,
708            241, 219, 179, 113, 96, 207, 245, 193, 3, 115, 166, 84, 177, 236, 191, 194, 134, 34,
709            214, 117, 182, 130, 121, 97, 9, 110, 170, 35, 44, 155, 205, 147, 180, 234, 188, 17, 39,
710            109, 146, 142, 68, 181, 186, 119, 197, 71, 45, 245, 246, 32, 139, 46, 197, 150, 12,
711            255, 110, 134, 99, 5, 139, 223, 13, 0, 1, 88, 32, 68, 43, 250, 251, 47, 151, 121, 58,
712            30, 144, 24, 129, 171, 35, 89, 56, 161, 112, 75, 91, 168, 201, 195, 121, 169, 155, 85,
713            104, 129, 60, 141, 161,
714        ];
715
716        let header_again: Header<()> = ciborium::de::from_reader(&bytes[..]).unwrap();
717        assert_eq!(header, header_again);
718    }
719
720    #[test]
721    fn decode_non_map_extensions() {
722        let signing_key = SigningKey::generate();
723
724        let mut header = Header::<()> {
725            version: 1,
726            verifying_key: signing_key.verifying_key(),
727            signature: None,
728            payload_size: 0,
729            payload_hash: None,
730            seq_num: 0,
731            backlink: None,
732            extensions: (),
733        };
734        header.sign(&signing_key);
735
736        let result = ciborium::de::from_reader::<Header<()>, _>(&header.to_bytes()[..]);
737        assert!(result.is_ok());
738    }
739
740    #[test]
741    fn unexpected_eof_when_incomplete() {
742        // ciborium should be able to detect an "Unexpected EOF" error if we're giving it an
743        // incomplete header.
744        let incomplete = [
745            137, 1, 88, 32, 228, 21, 196, 25, 12, 199, 241, 100, 122, 89, 46, 191, 142, 95, 144,
746        ];
747
748        let result: Result<Header<()>, _> = ciborium::de::from_reader(&incomplete[..]);
749        std::assert_matches!(result, Err(ciborium::de::Error::Io(_)));
750    }
751
752    #[test]
753    fn zero_sized_extensions() {
754        #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
755        struct Zilch;
756
757        #[derive(Clone, Debug, Serialize, Deserialize)]
758        struct ZeroSizedExtension {
759            field_a: [u8; 0],
760            field_b: (),
761            field_c: Zilch,
762        }
763
764        let signing_key = SigningKey::generate();
765        let body = Body::new(b"look, no bytes!");
766
767        let mut header = Header::<ZeroSizedExtension> {
768            version: 1,
769            verifying_key: signing_key.verifying_key(),
770            signature: None,
771            payload_size: body.size(),
772            payload_hash: Some(body.hash()),
773            seq_num: 0,
774            backlink: None,
775            extensions: ZeroSizedExtension {
776                field_a: [],
777                field_b: (),
778                field_c: Zilch,
779            },
780        };
781        header.sign(&signing_key);
782
783        let bytes = header.to_bytes();
784
785        // Make sure we skip the extensions field which means we only need 6 fields for the header.
786        //
787        // In CBOR this shows in the first byte where the "array" type + its length is declared
788        // (array(6)). In hex this would be represented by `86`, in decimal its `134`:
789        assert!(bytes[0] == 134);
790
791        // We correctly deserialize to the ZST.
792        let result: Header<ZeroSizedExtension> = ciborium::de::from_reader(&bytes[..]).unwrap();
793        assert_eq!(result.extensions.field_a.len(), 0);
794        assert_eq!(result.extensions.field_b, ());
795        assert_eq!(result.extensions.field_c, Zilch);
796    }
797}