massa_models/
block_header.rs

1use crate::block_id::{BlockId, BlockIdDeserializer, BlockIdSerializer};
2use crate::denunciation::{Denunciation, DenunciationDeserializer, DenunciationSerializer};
3use crate::endorsement::{
4    Endorsement, EndorsementDeserializerLW, EndorsementId, EndorsementSerializer,
5    EndorsementSerializerLW, SecureShareEndorsement,
6};
7use crate::secure_share::{
8    SecureShare, SecureShareContent, SecureShareDeserializer, SecureShareSerializer,
9};
10use crate::slot::{Slot, SlotDeserializer, SlotSerializer};
11use massa_hash::{Hash, HashDeserializer};
12use massa_serialization::{
13    Deserializer, OptionDeserializer, OptionSerializer, SerializeError, Serializer,
14    U32VarIntDeserializer, U32VarIntSerializer,
15};
16use massa_signature::PublicKey;
17use nom::branch::alt;
18use nom::bytes::complete::tag;
19use nom::error::{context, ContextError, ParseError};
20use nom::multi::{count, length_count};
21use nom::sequence::{preceded, tuple};
22use nom::{IResult, Parser};
23use serde::{Deserialize, Serialize};
24use std::collections::Bound::{Excluded, Included};
25use std::collections::HashSet;
26use std::fmt::Formatter;
27
28/// block header
29#[derive(Debug, Clone, Serialize, Deserialize)]
30pub struct BlockHeader {
31    /// current network version
32    pub current_version: u32,
33    /// announced network version
34    pub announced_version: Option<u32>,
35    /// slot
36    pub slot: Slot,
37    /// parents
38    pub parents: Vec<BlockId>,
39    /// all operations hash
40    pub operation_merkle_root: Hash,
41    /// endorsements
42    pub endorsements: Vec<SecureShareEndorsement>,
43    /// denunciations
44    pub denunciations: Vec<Denunciation>,
45}
46
47// TODO: gh-issue #3398
48#[cfg(any(test, feature = "test-exports"))]
49impl BlockHeader {
50    /// This is an intentional duplication of invariant checks. In production code,
51    /// these checks are dispersed throughout the deserialization process. This test-only function
52    /// allows all the checks to be in one place.
53    fn assert_invariants(
54        &self,
55        thread_count: u8,
56        endorsement_count: u32,
57    ) -> Result<(), Box<dyn std::error::Error>> {
58        if self.slot.period == 0 {
59            if !self.parents.is_empty() {
60                return Err("Invariant broken: genesis block with parent(s)".into());
61            }
62            if !self.endorsements.is_empty() {
63                return Err("Invariant broken: genesis block with endorsement(s)".into());
64            }
65        } else {
66            if self.parents.len() != thread_count as usize {
67                return Err(
68                    "Invariant broken: non-genesis block with incorrect number of parents".into(),
69                );
70            }
71            if self.endorsements.len() > endorsement_count as usize {
72                return Err("Invariant broken: endorsement count too high".into());
73            }
74
75            let parent_id = self.parents[self.slot.thread as usize];
76            for endo in self.endorsements.iter() {
77                if endo.content.endorsed_block != parent_id {
78                    return Err("Invariant broken: endorsement doesn't match parent".into());
79                }
80            }
81        }
82
83        // assert that the endorsement indexes are all unique...
84        let mut set = HashSet::new();
85        for endo in self.endorsements.iter() {
86            if !set.insert(endo.content.index) {
87                return Err("Endorsement duplicate index found".into());
88            }
89        }
90
91        for de in self.denunciations.iter() {
92            de.check_invariants()?;
93        }
94
95        Ok(())
96    }
97}
98
99/// BlockHeader wrapped up alongside verification data
100pub type SecuredHeader = SecureShare<BlockHeader, BlockId>;
101
102impl SecureShareContent for BlockHeader {
103    /// compute the signed hash
104    fn compute_signed_hash(&self, public_key: &PublicKey, content_hash: &Hash) -> Hash {
105        let mut signed_data: Vec<u8> = Vec::new();
106        signed_data.extend(public_key.to_bytes());
107        signed_data.extend(BlockHeaderDenunciationData::new(self.slot).to_bytes());
108        signed_data.extend(content_hash.to_bytes());
109        Hash::compute_from(&signed_data)
110    }
111}
112
113impl SecuredHeader {
114    /// gets the header fitness
115    pub fn get_fitness(&self) -> u64 {
116        (self.content.endorsements.len() as u64) + 1
117    }
118    // TODO: gh-issue #3398
119    #[allow(dead_code)]
120    #[cfg(any(test, feature = "test-exports"))]
121    pub(crate) fn assert_invariants(
122        &self,
123        thread_count: u8,
124        endorsement_count: u32,
125    ) -> Result<(), Box<dyn std::error::Error>> {
126        self.content
127            .assert_invariants(thread_count, endorsement_count)?;
128        self.verify_signature()
129            .map_err(|er| format!("{}", er).into())
130    }
131}
132
133/// Serializer for `BlockHeader`
134pub struct BlockHeaderSerializer {
135    slot_serializer: SlotSerializer,
136    endorsement_serializer: SecureShareSerializer,
137    endorsement_content_serializer: EndorsementSerializerLW,
138    denunciation_serializer: DenunciationSerializer,
139    u32_serializer: U32VarIntSerializer,
140    opt_serializer: OptionSerializer<u32, U32VarIntSerializer>,
141    block_id_serializer: BlockIdSerializer,
142}
143
144impl BlockHeaderSerializer {
145    /// Creates a new `BlockHeaderSerializer`
146    pub fn new() -> Self {
147        Self {
148            slot_serializer: SlotSerializer::new(),
149            endorsement_serializer: SecureShareSerializer::new(),
150            u32_serializer: U32VarIntSerializer::new(),
151            opt_serializer: OptionSerializer::new(U32VarIntSerializer),
152            endorsement_content_serializer: EndorsementSerializerLW::new(),
153            denunciation_serializer: DenunciationSerializer::new(),
154            block_id_serializer: BlockIdSerializer::new(),
155        }
156    }
157}
158
159impl Default for BlockHeaderSerializer {
160    fn default() -> Self {
161        Self::new()
162    }
163}
164
165impl Serializer<BlockHeader> for BlockHeaderSerializer {
166    /// ## Example:
167    /// ```rust
168    /// use massa_models::{block_id::BlockId, block_header::BlockHeader, block_header::BlockHeaderSerializer};
169    /// use massa_models::endorsement::{Endorsement, EndorsementSerializer};
170    /// use massa_models::secure_share::SecureShareContent;
171    /// use massa_models::{config::THREAD_COUNT, slot::Slot};
172    /// use massa_hash::Hash;
173    /// use massa_models::config::CHAINID;
174    /// use massa_signature::KeyPair;
175    /// use massa_serialization::Serializer;
176    ///
177    /// let keypair = KeyPair::generate(0).unwrap();
178    /// let parents = (0..THREAD_COUNT)
179    ///   .map(|i| BlockId::generate_from_hash(Hash::compute_from(&[i])))
180    ///   .collect();
181    /// let header = BlockHeader {
182    ///   current_version: 0,
183    ///   announced_version: None,
184    ///   slot: Slot::new(1, 1),
185    ///   parents,
186    ///   operation_merkle_root: Hash::compute_from("mno".as_bytes()),
187    ///   endorsements: vec![
188    ///     Endorsement::new_verifiable(
189    ///        Endorsement {
190    ///          slot: Slot::new(1, 1),
191    ///          index: 1,
192    ///          endorsed_block: BlockId::generate_from_hash(Hash::compute_from("blk1".as_bytes())),
193    ///        },
194    ///     EndorsementSerializer::new(),
195    ///     &keypair,
196    ///     *CHAINID
197    ///     )
198    ///     .unwrap(),
199    ///     Endorsement::new_verifiable(
200    ///       Endorsement {
201    ///         slot: Slot::new(4, 0),
202    ///         index: 3,
203    ///         endorsed_block: BlockId::generate_from_hash(Hash::compute_from("blk2".as_bytes())),
204    ///       },
205    ///     EndorsementSerializer::new(),
206    ///     &keypair,
207    ///     *CHAINID
208    ///     )
209    ///     .unwrap(),
210    ///    ],
211    ///   denunciations: vec![],
212    /// };
213    /// let mut buffer = vec![];
214    /// BlockHeaderSerializer::new().serialize(&header, &mut buffer).unwrap();
215    /// ```
216    fn serialize(&self, value: &BlockHeader, buffer: &mut Vec<u8>) -> Result<(), SerializeError> {
217        // network versions
218        self.u32_serializer
219            .serialize(&value.current_version, buffer)?;
220        self.opt_serializer
221            .serialize(&value.announced_version, buffer)?;
222
223        // slot
224        self.slot_serializer.serialize(&value.slot, buffer)?;
225
226        // parents (note: there should be none if slot period=0)
227        if value.parents.is_empty() {
228            buffer.push(0);
229        } else {
230            buffer.push(1);
231        }
232        for parent_h in value.parents.iter() {
233            self.block_id_serializer.serialize(parent_h, buffer)?;
234        }
235
236        // operations merkle root
237        buffer.extend(value.operation_merkle_root.to_bytes());
238
239        self.u32_serializer.serialize(
240            &value.endorsements.len().try_into().map_err(|err| {
241                SerializeError::GeneralError(format!("too many endorsements: {}", err))
242            })?,
243            buffer,
244        )?;
245
246        for endorsement in value.endorsements.iter() {
247            self.endorsement_serializer.serialize_with(
248                &self.endorsement_content_serializer,
249                endorsement,
250                buffer,
251            )?;
252        }
253        self.u32_serializer.serialize(
254            &value.denunciations.len().try_into().map_err(|err| {
255                SerializeError::GeneralError(format!("too many denunciations: {}", err))
256            })?,
257            buffer,
258        )?;
259        for denunciation in value.denunciations.iter() {
260            self.denunciation_serializer
261                .serialize(denunciation, buffer)?;
262        }
263
264        Ok(())
265    }
266}
267
268/// Deserializer for `BlockHeader`
269pub struct BlockHeaderDeserializer {
270    slot_deserializer: SlotDeserializer,
271    endorsement_serializer: EndorsementSerializer,
272    endorsement_len_deserializer: U32VarIntDeserializer,
273    hash_deserializer: HashDeserializer,
274    thread_count: u8,
275    endorsement_count: u32,
276    last_start_period: Option<u64>,
277    denunciation_len_deserializer: U32VarIntDeserializer,
278    denunciation_deserializer: DenunciationDeserializer,
279    network_versions_deserializer: U32VarIntDeserializer,
280    opt_deserializer: OptionDeserializer<u32, U32VarIntDeserializer>,
281    block_id_deserializer: BlockIdDeserializer,
282    chain_id: u64,
283}
284
285impl BlockHeaderDeserializer {
286    /// Creates a new `BlockHeaderDeserializer`
287    /// If last_start_period is Some(lsp), then the deserializer will check for valid (non)-genesis blocks
288    pub fn new(
289        thread_count: u8,
290        endorsement_count: u32,
291        max_denunciations_in_block_header: u32,
292        last_start_period: Option<u64>,
293        chain_id: u64,
294    ) -> Self {
295        Self {
296            slot_deserializer: SlotDeserializer::new(
297                (Included(0), Included(u64::MAX)),
298                (Included(0), Excluded(thread_count)),
299            ),
300            endorsement_serializer: EndorsementSerializer::new(),
301            endorsement_len_deserializer: U32VarIntDeserializer::new(
302                Included(0),
303                Included(endorsement_count),
304            ),
305            hash_deserializer: HashDeserializer::new(),
306            denunciation_len_deserializer: U32VarIntDeserializer::new(
307                Included(0),
308                Included(max_denunciations_in_block_header),
309            ),
310            network_versions_deserializer: U32VarIntDeserializer::new(
311                Included(0),
312                Included(u32::MAX),
313            ),
314            opt_deserializer: OptionDeserializer::new(U32VarIntDeserializer::new(
315                Included(0),
316                Included(u32::MAX),
317            )),
318            denunciation_deserializer: DenunciationDeserializer::new(
319                thread_count,
320                endorsement_count,
321            ),
322            block_id_deserializer: BlockIdDeserializer::new(),
323            thread_count,
324            endorsement_count,
325            last_start_period,
326            chain_id,
327        }
328    }
329}
330
331impl Deserializer<BlockHeader> for BlockHeaderDeserializer {
332    /// ## Example:
333    /// ```rust
334    /// use massa_models::block_header::{BlockHeader, BlockHeaderDeserializer, BlockHeaderSerializer};
335    /// use massa_models::block_id::{BlockId};
336    /// use massa_models::{config::THREAD_COUNT, slot::Slot, secure_share::SecureShareContent};
337    /// use massa_models::endorsement::{Endorsement, EndorsementSerializer};
338    /// use massa_hash::Hash;
339    /// use massa_models::config::CHAINID;
340    /// use massa_signature::KeyPair;
341    /// use massa_serialization::{Serializer, Deserializer, DeserializeError};
342    ///
343    /// let keypair = KeyPair::generate(0).unwrap();
344    /// let parents: Vec<BlockId> = (0..THREAD_COUNT)
345    ///   .map(|i| BlockId::generate_from_hash(Hash::compute_from(&[i])))
346    ///   .collect();
347    /// let header = BlockHeader {
348    ///   current_version: 0,
349    ///   announced_version: None,
350    ///   slot: Slot::new(1, 1),
351    ///   parents: parents.clone(),
352    ///   operation_merkle_root: Hash::compute_from("mno".as_bytes()),
353    ///   endorsements: vec![
354    ///     Endorsement::new_verifiable(
355    ///        Endorsement {
356    ///          slot: Slot::new(1, 1),
357    ///          index: 0,
358    ///          endorsed_block: parents[1].clone(),
359    ///        },
360    ///     EndorsementSerializer::new(),
361    ///     &keypair,
362    ///     *CHAINID
363    ///     )
364    ///     .unwrap(),
365    ///     Endorsement::new_verifiable(
366    ///       Endorsement {
367    ///         slot: Slot::new(1, 1),
368    ///         index: 1,
369    ///         endorsed_block: parents[1].clone(),
370    ///       },
371    ///     EndorsementSerializer::new(),
372    ///     &keypair,
373    ///     *CHAINID
374    ///     )
375    ///     .unwrap(),
376    ///    ],
377    ///    denunciations: vec![],
378    /// };
379    /// let mut buffer = vec![];
380    /// BlockHeaderSerializer::new().serialize(&header, &mut buffer).unwrap();
381    /// let (rest, deserialized_header) = BlockHeaderDeserializer::new(32, 9, 10, Some(0), 77).deserialize::<DeserializeError>(&buffer).unwrap();
382    /// assert_eq!(rest.len(), 0);
383    /// let mut buffer2 = Vec::new();
384    /// BlockHeaderSerializer::new().serialize(&deserialized_header, &mut buffer2).unwrap();
385    /// assert_eq!(buffer, buffer2);
386    /// ```
387    #[allow(clippy::type_complexity)]
388    fn deserialize<'a, E: ParseError<&'a [u8]> + ContextError<&'a [u8]>>(
389        &self,
390        buffer: &'a [u8],
391    ) -> IResult<&'a [u8], BlockHeader, E> {
392        let (rest, (current_version, announced_version, slot, parents, operation_merkle_root)): (
393            &[u8],
394            (u32, Option<u32>, Slot, Vec<BlockId>, Hash),
395        ) = context("Failed BlockHeader deserialization", |input| {
396            let (rest, (current_version, announced_version, slot, parents)) = tuple((
397                context("Failed current_version deserialization", |input| {
398                    self.network_versions_deserializer.deserialize(input)
399                }),
400                context("Failed announced_version deserialization", |input| {
401                    self.opt_deserializer.deserialize(input)
402                }),
403                context("Failed slot deserialization", |input| {
404                    self.slot_deserializer.deserialize(input)
405                }),
406                context(
407                    "Failed parents deserialization",
408                    alt((
409                        preceded(tag(&[0]), |input| Ok((input, Vec::new()))),
410                        preceded(
411                            tag(&[1]),
412                            count(
413                                context("Failed block_id deserialization", |input| {
414                                    self.block_id_deserializer.deserialize(input)
415                                }),
416                                self.thread_count as usize,
417                            ),
418                        ),
419                    )),
420                ),
421            ))
422            .parse(input)?;
423
424            // validate the parent/slot invariants before moving on to other fields
425            if let Some(last_start_period) = self.last_start_period {
426                if slot.period == last_start_period && !parents.is_empty() {
427                    return Err(nom::Err::Failure(ContextError::add_context(
428                        rest,
429                        "Genesis block cannot contain parents",
430                        ParseError::from_error_kind(rest, nom::error::ErrorKind::Fail),
431                    )));
432                } else if slot.period != last_start_period
433                    && parents.len() != self.thread_count as usize
434                {
435                    return Err(nom::Err::Failure(ContextError::add_context(
436                        rest,
437                        "Non-genesis block must have same numbers of parents as threads count",
438                        ParseError::from_error_kind(rest, nom::error::ErrorKind::Fail),
439                    )));
440                }
441            }
442
443            let (rest, merkle) = context("Failed operation_merkle_root", |input| {
444                self.hash_deserializer.deserialize(input)
445            })
446            .parse(rest)?;
447            Ok((
448                rest,
449                (current_version, announced_version, slot, parents, merkle),
450            ))
451        })
452        .parse(buffer)?;
453
454        if parents.is_empty() {
455            let res = BlockHeader {
456                current_version,
457                announced_version,
458                slot,
459                parents,
460                operation_merkle_root,
461                endorsements: Vec::new(),
462                denunciations: Vec::new(),
463            };
464
465            // TODO: gh-issue #3398
466            #[cfg(any(test, feature = "test-exports"))]
467            res.assert_invariants(self.thread_count, self.endorsement_count)
468                .unwrap();
469
470            // As we have 0 endorsements & 0 denunciations, rest = [0, 0] (length 0 & length 0)
471            // As we want to return an empty "res" we use nom tag
472            let (rest2, _) = tag(&[0, 0])(rest)?;
473            return Ok((rest2, res));
474        }
475
476        // Now deser the endorsements (which were light-weight serialized)
477        let endorsement_deserializer = SecureShareDeserializer::new(
478            EndorsementDeserializerLW::new(
479                self.endorsement_count,
480                slot,
481                parents[slot.thread as usize],
482            ),
483            self.chain_id,
484        );
485
486        let parent_id = parents[slot.thread as usize];
487        let (rest, endorsements): (&[u8], Vec<SecureShare<Endorsement, EndorsementId>>) = context(
488            "Failed endorsements deserialization",
489            length_count::<&[u8], SecureShare<Endorsement, EndorsementId>, u32, E, _, _>(
490                context("Failed length deserialization", |input| {
491                    self.endorsement_len_deserializer.deserialize(input)
492                }),
493                context("Failed endorsement deserialization", |input| {
494                    let (rest, endo) = endorsement_deserializer
495                        .deserialize_with(&self.endorsement_serializer, input)?;
496
497                    if endo.content.endorsed_block != parent_id {
498                        return Err(nom::Err::Failure(ContextError::add_context(
499                            rest,
500                            "Endorsement does not match block parents",
501                            ParseError::from_error_kind(rest, nom::error::ErrorKind::Fail),
502                        )));
503                    }
504
505                    Ok((rest, endo))
506                }),
507            ),
508        )
509        .parse(rest)?;
510
511        let mut set = HashSet::new();
512        for end in endorsements.iter() {
513            if !set.insert(end.content.index) {
514                return Err(nom::Err::Failure(ContextError::add_context(
515                    rest,
516                    "Duplicate endorsement index found",
517                    ParseError::from_error_kind(rest, nom::error::ErrorKind::Fail),
518                )));
519            }
520        }
521
522        let (rest, denunciations): (&[u8], Vec<Denunciation>) = context(
523            "Failed denunciations deserialization",
524            length_count::<&[u8], Denunciation, u32, E, _, _>(
525                context("Failed length deserialization", |input| {
526                    let (res, count) = self.denunciation_len_deserializer.deserialize(input)?;
527                    IResult::Ok((res, count))
528                }),
529                context("Failed denunciation deserialization", |input| {
530                    self.denunciation_deserializer.deserialize(input)
531                }),
532            ),
533        )
534        .parse(rest)?;
535
536        let header = BlockHeader {
537            current_version,
538            announced_version,
539            slot,
540            parents,
541            operation_merkle_root,
542            endorsements,
543            denunciations,
544        };
545
546        // TODO: gh-issue #3398
547        #[cfg(any(test, feature = "test-exports"))]
548        header
549            .assert_invariants(self.thread_count, self.endorsement_count)
550            .unwrap();
551
552        Ok((rest, header))
553    }
554}
555
556impl std::fmt::Display for BlockHeader {
557    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
558        writeln!(
559            f,
560            "\t(period: {}, thread: {})",
561            self.slot.period, self.slot.thread,
562        )?;
563        writeln!(f, "\tMerkle root: {}", self.operation_merkle_root,)?;
564        writeln!(f, "\tParents: ")?;
565        for id in self.parents.iter() {
566            let str_id = id.to_string();
567            writeln!(f, "\t\t{}", str_id)?;
568        }
569        if self.parents.is_empty() {
570            writeln!(f, "No parents found: This is a genesis header")?;
571        }
572        writeln!(f, "\tEndorsements:")?;
573        for ed in self.endorsements.iter() {
574            writeln!(f, "\t\t-----")?;
575            writeln!(f, "\t\tId: {}", ed.id)?;
576            writeln!(f, "\t\tIndex: {}", ed.content.index)?;
577            writeln!(f, "\t\tEndorsed slot: {}", ed.content.slot)?;
578            writeln!(
579                f,
580                "\t\tEndorser's public key: {}",
581                ed.content_creator_pub_key
582            )?;
583            writeln!(f, "\t\tEndorsed block: {}", ed.content.endorsed_block)?;
584            writeln!(f, "\t\tSignature: {}", ed.signature)?;
585        }
586        if self.endorsements.is_empty() {
587            writeln!(f, "\tNo endorsements found")?;
588        }
589        Ok(())
590    }
591}
592
593/// A denunciation data for block header
594#[derive(Debug)]
595pub struct BlockHeaderDenunciationData {
596    slot: Slot,
597}
598
599impl BlockHeaderDenunciationData {
600    /// Create a new DenunciationData for block hedader
601    pub fn new(slot: Slot) -> Self {
602        Self { slot }
603    }
604
605    /// Get byte array
606    pub fn to_bytes(&self) -> Vec<u8> {
607        let mut buf = Vec::new();
608        buf.extend(self.slot.to_bytes_key());
609        buf
610    }
611}
612
613#[cfg(test)]
614mod test {
615    use super::*;
616    use massa_serialization::DeserializeError;
617    use serde_json::Value;
618
619    use crate::config::{
620        CHAINID, ENDORSEMENT_COUNT, MAX_DENUNCIATIONS_PER_BLOCK_HEADER, THREAD_COUNT,
621    };
622
623    use crate::test_exports::{
624        gen_block_headers_for_denunciation, gen_endorsements_for_denunciation,
625    };
626    use massa_signature::{verify_signature_batch, KeyPair};
627
628    // Only for testing purpose
629    impl PartialEq for BlockHeader {
630        fn eq(&self, other: &Self) -> bool {
631            self.slot == other.slot
632                && self.parents == other.parents
633                && self.operation_merkle_root == other.operation_merkle_root
634                && self.endorsements == other.endorsements
635                && self.denunciations == other.denunciations
636        }
637    }
638
639    #[test]
640    fn test_block_header_ser_der() {
641        let keypair = KeyPair::generate(0).unwrap();
642
643        let slot = Slot::new(7, 1);
644        let parents_1: Vec<BlockId> = (0..THREAD_COUNT)
645            .map(|i| BlockId::generate_from_hash(Hash::compute_from(&[i])))
646            .collect();
647
648        let endorsement_1 = Endorsement {
649            slot,
650            index: 1,
651            endorsed_block: parents_1[1],
652        };
653
654        assert_eq!(parents_1[1], endorsement_1.endorsed_block);
655
656        let s_endorsement_1: SecureShareEndorsement = Endorsement::new_verifiable(
657            endorsement_1,
658            EndorsementSerializer::new(),
659            &keypair,
660            *CHAINID,
661        )
662        .unwrap();
663
664        let (slot_a, _, s_header_1, s_header_2, _) = gen_block_headers_for_denunciation(None, None);
665        assert!(slot_a < slot);
666        let de_a = Denunciation::try_from((&s_header_1, &s_header_2)).unwrap();
667        let (slot_b, _, s_endo_1, s_endo_2, _) = gen_endorsements_for_denunciation(None, None);
668        assert!(slot_b < slot);
669        let de_b = Denunciation::try_from((&s_endo_1, &s_endo_2)).unwrap();
670
671        let block_header_1 = BlockHeader {
672            current_version: 0,
673            announced_version: None,
674            slot,
675            parents: parents_1,
676            operation_merkle_root: Hash::compute_from("mno".as_bytes()),
677            endorsements: vec![s_endorsement_1],
678            denunciations: vec![de_a, de_b],
679        };
680
681        let mut buffer = Vec::new();
682        let ser = BlockHeaderSerializer::new();
683        ser.serialize(&block_header_1, &mut buffer).unwrap();
684        let der = BlockHeaderDeserializer::new(
685            THREAD_COUNT,
686            ENDORSEMENT_COUNT,
687            MAX_DENUNCIATIONS_PER_BLOCK_HEADER,
688            None,
689            *CHAINID,
690        );
691
692        let (rem, block_header_der) = der.deserialize::<DeserializeError>(&buffer).unwrap();
693
694        assert!(rem.is_empty());
695        assert_eq!(block_header_1, block_header_der);
696    }
697
698    #[test]
699    fn test_block_header_no_parents_ser_der() {
700        let slot = Slot::new(0, 1);
701        let block_header_1 = BlockHeader {
702            current_version: 0,
703            announced_version: None,
704            slot,
705            parents: vec![],
706            operation_merkle_root: Hash::compute_from("mno".as_bytes()),
707            endorsements: vec![],
708            denunciations: vec![],
709        };
710
711        let mut buffer = Vec::new();
712        let ser = BlockHeaderSerializer::new();
713        ser.serialize(&block_header_1, &mut buffer).unwrap();
714        let der = BlockHeaderDeserializer::new(
715            THREAD_COUNT,
716            ENDORSEMENT_COUNT,
717            MAX_DENUNCIATIONS_PER_BLOCK_HEADER,
718            None,
719            *CHAINID,
720        );
721
722        let (rem, block_header_der) = der.deserialize::<DeserializeError>(&buffer).unwrap();
723
724        assert!(rem.is_empty());
725        assert_eq!(block_header_1, block_header_der);
726    }
727
728    #[test]
729    fn test_verify_sig_batch() {
730        let (_slot, _keypair, secured_header_1, secured_header_2, secured_header_3) =
731            gen_block_headers_for_denunciation(None, None);
732
733        // Test with batch len == 1 (no // verif)
734        let batch_1 = [(
735            secured_header_1.compute_signed_hash(),
736            secured_header_1.signature,
737            secured_header_1.content_creator_pub_key,
738        )];
739        verify_signature_batch(&batch_1).unwrap();
740
741        // Test with batch len > 1 (// verif)
742        let batch_2 = [
743            (
744                secured_header_1.compute_signed_hash(),
745                secured_header_1.signature,
746                secured_header_1.content_creator_pub_key,
747            ),
748            (
749                secured_header_2.compute_signed_hash(),
750                secured_header_2.signature,
751                secured_header_2.content_creator_pub_key,
752            ),
753            (
754                secured_header_3.compute_signed_hash(),
755                secured_header_3.signature,
756                secured_header_3.content_creator_pub_key,
757            ),
758        ];
759        verify_signature_batch(&batch_2).unwrap();
760    }
761
762    #[test]
763    fn test_block_header_serde() {
764        let keypair = KeyPair::generate(0).unwrap();
765
766        let slot = Slot::new(7, 1);
767        let parents_1: Vec<BlockId> = (0..THREAD_COUNT)
768            .map(|i| BlockId::generate_from_hash(Hash::compute_from(&[i])))
769            .collect();
770
771        let endorsement_1 = Endorsement {
772            slot,
773            index: 1,
774            endorsed_block: parents_1[1],
775        };
776
777        assert_eq!(parents_1[1], endorsement_1.endorsed_block);
778
779        let s_endorsement_1: SecureShareEndorsement = Endorsement::new_verifiable(
780            endorsement_1,
781            EndorsementSerializer::new(),
782            &keypair,
783            *CHAINID,
784        )
785        .unwrap();
786
787        let (slot_a, _, s_header_1, s_header_2, _) = gen_block_headers_for_denunciation(None, None);
788        assert!(slot_a < slot);
789        let de_a = Denunciation::try_from((&s_header_1, &s_header_2)).unwrap();
790        let (slot_b, _, s_endo_1, s_endo_2, _) = gen_endorsements_for_denunciation(None, None);
791        assert!(slot_b < slot);
792        let de_b = Denunciation::try_from((&s_endo_1, &s_endo_2)).unwrap();
793
794        let block_header_1 = BlockHeader {
795            current_version: 0,
796            announced_version: None,
797            slot,
798            parents: parents_1,
799            operation_merkle_root: Hash::compute_from("mno".as_bytes()),
800            endorsements: vec![s_endorsement_1],
801            denunciations: vec![de_a, de_b],
802        };
803
804        let serialized_block_header = serde_json::to_string(&block_header_1).unwrap();
805        let res_block_header: Value = serde_json::from_str(&serialized_block_header).unwrap();
806        // check equality
807        assert_eq!(
808            block_header_1.current_version,
809            res_block_header["current_version"]
810        );
811        assert_eq!(
812            block_header_1.slot.period,
813            res_block_header["slot"]["period"]
814        );
815        assert_eq!(
816            block_header_1.slot.thread,
817            res_block_header["slot"]["thread"]
818        );
819    }
820}