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