massa_models/
denunciation.rs

1//! Copyright (c) 2022 MASSA LABS <info@massa.net>
2
3//! Denunciation intro
4//!
5//! Currently, nothing prevents a user from producing multiple blocks (for the same Slot) or endorsing a block multiple times.
6//! If an invalid Denunciation will just be ignored, a valid Denunciation will slash some locked rolls.
7//! Note that this proposal aims at dissuading 'rational' users from causing damage but does help against 'byzantine' actors
8//! that just want to disturb or break the blockclique (at any cost).
9//!
10//! Denunciation structure
11//!
12//! A denunciation embeds some information such as
13//! Slot, the slot from the block header or the endorsements (+ index)
14//! A public key: the public key of the secure share endorsement or secured header
15//! 2 Hashes & 2 Signatures
16//!
17//! All of this constitutes a proof (and is verifiable) that a user produced multiple (at least 2) blocks or endorsements
18//!
19//! Denunciation creation
20//!
21//! Denunciations are created in a Denunciation pool, receiving new blocks & block headers & endorsements from various places.
22//! The denunciation pool is also responsible of returning a list of denunciations to insert into a new block header.
23//! After execution, the denunciation is kept for some time inside `executed_denunciations`
24//! in order to prevent multiple executions. Note that this structure is part of the final state hash and thus is bootstrapped.
25//!
26//! Denunciation execution
27//!
28//! When a Denunciation is proven valid, we then need to ensure that the user has sufficient funds. If we cannot deduct the funds on node
29//! balance or staked rolls, we will use 'locked' rolls` (or Deferred credits).
30//! Selling a roll will lock it for some time (== 4 cycles). Note that it restricts the time,
31//! a denunciation can be produced (A constant value will be defined for this).
32//!
33//! Punishment model
34//!
35//! A valid denunciation slashes locked rolls only. It does not forfeit slot rewards
36//! (`EndorsementCreatorReward`, `EndorsementTargetReward`, `BlockCreatorReward`) for the
37//! denouncing block. A validator denounced in a block may still receive those rewards in the
38//! same slot. That is intentional: excluding denounced addresses from same-slot rewards would
39//! make the effective penalty depend on whether they were also selected by draws for that slot,
40//! which is not a coherent punishment model.
41
42#![allow(missing_docs)]
43
44use std::cmp::Ordering;
45use std::ops::Bound::{Excluded, Included};
46
47use nom::{
48    error::{context, ContextError, ParseError},
49    sequence::tuple,
50    IResult, Parser,
51};
52use num_enum::{IntoPrimitive, TryFromPrimitive};
53use serde::{Deserialize, Serialize};
54use thiserror::Error;
55
56use crate::block_header::{BlockHeaderDenunciationData, SecuredHeader};
57use crate::endorsement::{EndorsementDenunciationData, SecureShareEndorsement};
58use crate::slot::{Slot, SlotDeserializer, SlotSerializer};
59
60use crate::secure_share::Id;
61use massa_hash::{Hash, HashDeserializer, HashSerializer};
62use massa_serialization::{
63    Deserializer, SerializeError, Serializer, U32VarIntDeserializer, U32VarIntSerializer,
64};
65use massa_signature::{
66    MassaSignatureError, PublicKey, PublicKeyDeserializer, Signature, SignatureDeserializer,
67};
68use variant_count::VariantCount;
69
70/// A Variant of Denunciation enum for endorsement
71#[allow(dead_code)]
72#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
73pub struct EndorsementDenunciation {
74    public_key: PublicKey,
75    slot: Slot,
76    index: u32,
77    hash_1: Hash,
78    hash_2: Hash,
79    signature_1: Signature,
80    signature_2: Signature,
81}
82
83impl EndorsementDenunciation {
84    /// Rebuild full hash of SecureShareEndorsement from given arguments
85    fn compute_hash_for_sig_verif(
86        public_key: &PublicKey,
87        slot: &Slot,
88        index: &u32,
89        content_hash: &Hash,
90    ) -> Hash {
91        let mut hash_data = Vec::new();
92        // Public key
93        hash_data.extend(public_key.to_bytes());
94        // Ser slot & index
95        let denunciation_data = EndorsementDenunciationData::new(*slot, *index);
96        hash_data.extend(&denunciation_data.to_bytes());
97        // Add content hash
98        hash_data.extend(content_hash.to_bytes());
99        Hash::compute_from(&hash_data)
100    }
101
102    // Getters (for GRPC From)
103    pub fn get_public_key(&self) -> &PublicKey {
104        &self.public_key
105    }
106    pub fn get_slot(&self) -> &Slot {
107        &self.slot
108    }
109    pub fn get_index(&self) -> &u32 {
110        &self.index
111    }
112    pub fn get_hash_1(&self) -> &Hash {
113        &self.hash_1
114    }
115    pub fn get_hash_2(&self) -> &Hash {
116        &self.hash_2
117    }
118    pub fn get_signature_1(&self) -> &Signature {
119        &self.signature_1
120    }
121    pub fn get_signature_2(&self) -> &Signature {
122        &self.signature_2
123    }
124}
125
126/// A Variant of Denunciation enum for block header
127#[allow(dead_code)]
128#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
129pub struct BlockHeaderDenunciation {
130    public_key: PublicKey,
131    slot: Slot,
132    hash_1: Hash,
133    hash_2: Hash,
134    signature_1: Signature,
135    signature_2: Signature,
136}
137
138impl BlockHeaderDenunciation {
139    /// Rebuild full hash of SecuredHeader from given arguments
140    fn compute_hash_for_sig_verif(
141        public_key: &PublicKey,
142        slot: &Slot,
143        content_hash: &Hash,
144    ) -> Hash {
145        let mut hash_data = Vec::new();
146        // Public key
147        hash_data.extend(public_key.to_bytes());
148        // Ser slot
149        let de_data = BlockHeaderDenunciationData::new(*slot);
150        hash_data.extend(de_data.to_bytes());
151        // Add content hash
152        hash_data.extend(content_hash.to_bytes());
153        Hash::compute_from(&hash_data)
154    }
155
156    // Getters (for GRPC From)
157    pub fn get_public_key(&self) -> &PublicKey {
158        &self.public_key
159    }
160    pub fn get_slot(&self) -> &Slot {
161        &self.slot
162    }
163    pub fn get_hash_1(&self) -> &Hash {
164        &self.hash_1
165    }
166    pub fn get_hash_2(&self) -> &Hash {
167        &self.hash_2
168    }
169    pub fn get_signature_1(&self) -> &Signature {
170        &self.signature_1
171    }
172    pub fn get_signature_2(&self) -> &Signature {
173        &self.signature_2
174    }
175}
176
177/// A denunciation enum
178#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
179#[allow(missing_docs)]
180pub enum Denunciation {
181    Endorsement(EndorsementDenunciation),
182    BlockHeader(BlockHeaderDenunciation),
183}
184
185#[allow(dead_code)]
186impl Denunciation {
187    /// Check if it is a Denunciation of several endorsements
188    pub fn is_for_endorsement(&self) -> bool {
189        matches!(self, Denunciation::Endorsement(_))
190    }
191
192    /// Check if it is a Denunciation of several block headers
193    pub fn is_for_block_header(&self) -> bool {
194        matches!(self, Denunciation::BlockHeader(_))
195    }
196
197    /// Check if it is a Denunciation for this endorsement
198    pub fn is_also_for_endorsement(
199        &self,
200        s_endorsement: &SecureShareEndorsement,
201    ) -> Result<bool, DenunciationError> {
202        match self {
203            Denunciation::BlockHeader(_) => Ok(false),
204            Denunciation::Endorsement(endo_de) => {
205                let content_hash = s_endorsement.id.get_hash();
206
207                let hash = EndorsementDenunciation::compute_hash_for_sig_verif(
208                    &endo_de.public_key,
209                    &endo_de.slot,
210                    &endo_de.index,
211                    content_hash,
212                );
213
214                Ok(endo_de.slot == s_endorsement.content.slot
215                    && endo_de.index == s_endorsement.content.index
216                    && endo_de.public_key == s_endorsement.content_creator_pub_key
217                    && endo_de.hash_1 != *content_hash
218                    && endo_de.hash_2 != *content_hash
219                    && endo_de
220                        .public_key
221                        .verify_signature(&hash, &s_endorsement.signature)
222                        .is_ok())
223            }
224        }
225    }
226
227    /// Check if it is a Denunciation for this block header
228    pub fn is_also_for_block_header(
229        &self,
230        s_block_header: &SecuredHeader,
231    ) -> Result<bool, DenunciationError> {
232        match self {
233            Denunciation::Endorsement(_) => Ok(false),
234            Denunciation::BlockHeader(endo_bh) => {
235                let content_hash = s_block_header.id.get_hash();
236
237                let hash = BlockHeaderDenunciation::compute_hash_for_sig_verif(
238                    &endo_bh.public_key,
239                    &endo_bh.slot,
240                    content_hash,
241                );
242
243                Ok(endo_bh.slot == s_block_header.content.slot
244                    && endo_bh.public_key == s_block_header.content_creator_pub_key
245                    && endo_bh.hash_1 != *content_hash
246                    && endo_bh.hash_2 != *content_hash
247                    && endo_bh
248                        .public_key
249                        .verify_signature(&hash, &s_block_header.signature)
250                        .is_ok())
251            }
252        }
253    }
254
255    /// Check if Denunciation is valid
256    /// Should be used if received from the network (prevent against invalid or attacker crafted denunciation)
257    pub fn is_valid(&self) -> bool {
258        let (signature_1, signature_2, hash_1, hash_2, public_key) = match self {
259            Denunciation::Endorsement(de) => {
260                let hash_1 = EndorsementDenunciation::compute_hash_for_sig_verif(
261                    &de.public_key,
262                    &de.slot,
263                    &de.index,
264                    &de.hash_1,
265                );
266                let hash_2 = EndorsementDenunciation::compute_hash_for_sig_verif(
267                    &de.public_key,
268                    &de.slot,
269                    &de.index,
270                    &de.hash_2,
271                );
272
273                (
274                    de.signature_1,
275                    de.signature_2,
276                    hash_1,
277                    hash_2,
278                    de.public_key,
279                )
280            }
281            Denunciation::BlockHeader(de) => {
282                let hash_1 = BlockHeaderDenunciation::compute_hash_for_sig_verif(
283                    &de.public_key,
284                    &de.slot,
285                    &de.hash_1,
286                );
287                let hash_2 = BlockHeaderDenunciation::compute_hash_for_sig_verif(
288                    &de.public_key,
289                    &de.slot,
290                    &de.hash_2,
291                );
292
293                (
294                    de.signature_1,
295                    de.signature_2,
296                    hash_1,
297                    hash_2,
298                    de.public_key,
299                )
300            }
301        };
302
303        hash_1 != hash_2
304            && public_key.verify_signature(&hash_1, &signature_1).is_ok()
305            && public_key.verify_signature(&hash_2, &signature_2).is_ok()
306    }
307
308    /// Get Denunciation slot ref
309    pub fn get_slot(&self) -> &Slot {
310        match self {
311            Denunciation::Endorsement(de) => &de.slot,
312            Denunciation::BlockHeader(de) => &de.slot,
313        }
314    }
315
316    /// Get field: index (return None for a block header denunciation)
317    pub fn get_index(&self) -> Option<&u32> {
318        match self {
319            Denunciation::BlockHeader(_) => None,
320            Denunciation::Endorsement(de) => Some(&de.index),
321        }
322    }
323
324    /// Get Denunciation public key ref
325    pub fn get_public_key(&self) -> &PublicKey {
326        match self {
327            Denunciation::Endorsement(de) => &de.public_key,
328            Denunciation::BlockHeader(de) => &de.public_key,
329        }
330    }
331
332    /// Check if denunciation has expired given a slot period
333    /// Note that slot_period can be:
334    /// * A final slot period (for example in order to cleanup denunciation pool caches)
335    /// * A block slot period (in execution (execute_denunciation(...)))
336    pub fn is_expired(
337        denunciation_slot_period: &u64,
338        slot_period: &u64,
339        denunciation_expire_periods: &u64,
340    ) -> bool {
341        slot_period.checked_sub(*denunciation_slot_period) > Some(*denunciation_expire_periods)
342    }
343}
344
345/// Create a new Denunciation from 2 SecureShareEndorsement
346impl TryFrom<(&SecureShareEndorsement, &SecureShareEndorsement)> for Denunciation {
347    type Error = DenunciationError;
348
349    fn try_from(
350        (s_e1, s_e2): (&SecureShareEndorsement, &SecureShareEndorsement),
351    ) -> Result<Self, Self::Error> {
352        // In order to create a Denunciation, there should be the same
353        // slot, index & public key
354        if s_e1.content.slot != s_e2.content.slot
355            || s_e1.content.index != s_e2.content.index
356            || s_e1.content_creator_pub_key != s_e2.content_creator_pub_key
357            || s_e1.id == s_e2.id
358        {
359            return Err(DenunciationError::InvalidInput(format!(
360                "Not the same slot, index or public key or same hash for {:?} & {:?}",
361                s_e1, s_e2
362            )));
363        }
364
365        // Check sig of s_e1 with s_e1.public_key, s_e1.slot, s_e1.index
366        let s_e1_hash_content = s_e1.id.get_hash();
367        let s_e1_hash = EndorsementDenunciation::compute_hash_for_sig_verif(
368            &s_e1.content_creator_pub_key,
369            &s_e1.content.slot,
370            &s_e1.content.index,
371            s_e1_hash_content,
372        );
373        // Check sig of s_e2 but with s_e1.public_key, s_e1.slot, s_e1.index
374        let s_e2_hash_content = s_e2.id.get_hash();
375        let s_e2_hash = EndorsementDenunciation::compute_hash_for_sig_verif(
376            &s_e1.content_creator_pub_key,
377            &s_e1.content.slot,
378            &s_e1.content.index,
379            s_e2_hash_content,
380        );
381
382        s_e1.content_creator_pub_key
383            .verify_signature(&s_e1_hash, &s_e1.signature)?;
384        s_e1.content_creator_pub_key
385            .verify_signature(&s_e2_hash, &s_e2.signature)?;
386
387        Ok(Denunciation::Endorsement(EndorsementDenunciation {
388            public_key: s_e1.content_creator_pub_key,
389            slot: s_e1.content.slot,
390            index: s_e1.content.index,
391            signature_1: s_e1.signature,
392            signature_2: s_e2.signature,
393            hash_1: *s_e1_hash_content,
394            hash_2: *s_e2_hash_content,
395        }))
396    }
397}
398
399/// Create a new Denunciation from 2 SecureHeader
400impl TryFrom<(&SecuredHeader, &SecuredHeader)> for Denunciation {
401    type Error = DenunciationError;
402
403    fn try_from((s_bh1, s_bh2): (&SecuredHeader, &SecuredHeader)) -> Result<Self, Self::Error> {
404        // Cannot use the same block header twice
405        // In order to create a Denunciation, there should be the same slot & public key
406        if s_bh1.content.slot != s_bh2.content.slot
407            || s_bh1.content_creator_pub_key != s_bh2.content_creator_pub_key
408            || s_bh1.id == s_bh2.id
409        {
410            return Err(DenunciationError::InvalidInput(format!(
411                "Not the same slot or public key or same hash for {:?} & {:?}",
412                s_bh1, s_bh2
413            )));
414        }
415
416        // Check sig of s_bh2 but with s_bh1.public_key, s_bh1.slot, s_bh1.index
417        let s_bh1_hash_content = s_bh1.id.get_hash();
418        let s_bh1_hash = BlockHeaderDenunciation::compute_hash_for_sig_verif(
419            &s_bh1.content_creator_pub_key,
420            &s_bh1.content.slot,
421            s_bh1_hash_content,
422        );
423        let s_bh2_hash_content = s_bh2.id.get_hash();
424        let s_bh2_hash = BlockHeaderDenunciation::compute_hash_for_sig_verif(
425            &s_bh1.content_creator_pub_key,
426            &s_bh1.content.slot,
427            s_bh2_hash_content,
428        );
429
430        s_bh1
431            .content_creator_pub_key
432            .verify_signature(&s_bh1_hash, &s_bh1.signature)?;
433        s_bh1
434            .content_creator_pub_key
435            .verify_signature(&s_bh2_hash, &s_bh2.signature)?;
436
437        Ok(Denunciation::BlockHeader(BlockHeaderDenunciation {
438            public_key: s_bh1.content_creator_pub_key,
439            slot: s_bh1.content.slot,
440            signature_1: s_bh1.signature,
441            signature_2: s_bh2.signature,
442            hash_1: *s_bh1_hash_content,
443            hash_2: *s_bh2_hash_content,
444        }))
445    }
446}
447
448#[allow(missing_docs)]
449#[derive(IntoPrimitive, Debug, TryFromPrimitive, VariantCount)]
450#[repr(u32)]
451pub enum DenunciationTypeId {
452    BlockHeader = 0,
453    Endorsement = 1,
454}
455
456impl From<&Denunciation> for DenunciationTypeId {
457    fn from(value: &Denunciation) -> Self {
458        match value {
459            Denunciation::Endorsement(_) => DenunciationTypeId::Endorsement,
460            Denunciation::BlockHeader(_) => DenunciationTypeId::BlockHeader,
461        }
462    }
463}
464
465/// Denunciation error
466#[allow(missing_docs)]
467#[derive(Error, Debug)]
468pub enum DenunciationError {
469    #[error("Invalid endorsements or block headers, cannot create denunciation: {0}")]
470    InvalidInput(String),
471    #[error("signature error: {0}")]
472    Signature(#[from] MassaSignatureError),
473    #[error("serialization error: {0}")]
474    Serialization(#[from] SerializeError),
475}
476
477// Serialization / Deserialization
478
479/// Serializer for `EndorsementDenunciation`
480struct EndorsementDenunciationSerializer {
481    slot_serializer: SlotSerializer,
482    u32_serializer: U32VarIntSerializer,
483    hash_serializer: HashSerializer,
484}
485
486impl EndorsementDenunciationSerializer {
487    /// Creates a new `EndorsementDenunciationSerializer`
488    const fn new() -> Self {
489        Self {
490            slot_serializer: SlotSerializer::new(),
491            u32_serializer: U32VarIntSerializer::new(),
492            hash_serializer: HashSerializer::new(),
493        }
494    }
495}
496
497impl Default for EndorsementDenunciationSerializer {
498    fn default() -> Self {
499        Self::new()
500    }
501}
502
503impl Serializer<EndorsementDenunciation> for EndorsementDenunciationSerializer {
504    fn serialize(
505        &self,
506        value: &EndorsementDenunciation,
507        buffer: &mut Vec<u8>,
508    ) -> Result<(), SerializeError> {
509        buffer.extend(value.public_key.to_bytes());
510        self.slot_serializer.serialize(&value.slot, buffer)?;
511        self.u32_serializer.serialize(&value.index, buffer)?;
512        self.hash_serializer.serialize(&value.hash_1, buffer)?;
513        self.hash_serializer.serialize(&value.hash_2, buffer)?;
514        buffer.extend(value.signature_1.to_bytes());
515        buffer.extend(value.signature_2.to_bytes());
516        Ok(())
517    }
518}
519
520/// Deserializer for `EndorsementDenunciation`
521struct EndorsementDenunciationDeserializer {
522    slot_deserializer: SlotDeserializer,
523    index_deserializer: U32VarIntDeserializer,
524    hash_deserializer: HashDeserializer,
525    pubkey_deserializer: PublicKeyDeserializer,
526    signature_deserializer: SignatureDeserializer,
527}
528
529impl EndorsementDenunciationDeserializer {
530    /// Creates a new `EndorsementDeserializer`
531    const fn new(thread_count: u8, endorsement_count: u32) -> Self {
532        EndorsementDenunciationDeserializer {
533            slot_deserializer: SlotDeserializer::new(
534                (Included(0), Included(u64::MAX)),
535                (Included(0), Excluded(thread_count)),
536            ),
537            index_deserializer: U32VarIntDeserializer::new(
538                Included(0),
539                Excluded(endorsement_count),
540            ),
541            hash_deserializer: HashDeserializer::new(),
542            pubkey_deserializer: PublicKeyDeserializer::new(),
543            signature_deserializer: SignatureDeserializer::new(),
544        }
545    }
546}
547
548impl Deserializer<EndorsementDenunciation> for EndorsementDenunciationDeserializer {
549    fn deserialize<'a, E: ParseError<&'a [u8]> + ContextError<&'a [u8]>>(
550        &self,
551        buffer: &'a [u8],
552    ) -> IResult<&'a [u8], EndorsementDenunciation, E> {
553        context(
554            "Failed Endorsement Denunciation deserialization",
555            tuple((
556                context("Failed public key deserialization", |input| {
557                    self.pubkey_deserializer.deserialize(input)
558                }),
559                context("Failed slot deserialization", |input| {
560                    self.slot_deserializer.deserialize(input)
561                }),
562                context("Failed slot deserialization", |input| {
563                    self.index_deserializer.deserialize(input)
564                }),
565                context("Failed hash 1 deserialization", |input| {
566                    self.hash_deserializer.deserialize(input)
567                }),
568                context("Failed hash 2 deserialization", |input| {
569                    self.hash_deserializer.deserialize(input)
570                }),
571                context("Failed signature 1 deserialization", |input| {
572                    self.signature_deserializer.deserialize(input)
573                }),
574                context("Failed signature 2 deserialization", |input| {
575                    self.signature_deserializer.deserialize(input)
576                }),
577            )),
578        )
579        .map(
580            |(public_key, slot, index, hash_1, hash_2, signature_1, signature_2)| {
581                EndorsementDenunciation {
582                    public_key,
583                    slot,
584                    index,
585                    hash_1,
586                    hash_2,
587                    signature_1,
588                    signature_2,
589                }
590            },
591        )
592        .parse(buffer)
593    }
594}
595
596/// Serializer for `BlockHeaderDenunciation`
597struct BlockHeaderDenunciationSerializer {
598    slot_serializer: SlotSerializer,
599    hash_serializer: HashSerializer,
600}
601
602impl BlockHeaderDenunciationSerializer {
603    /// Creates a new `BlockHeaderDenunciationSerializer`
604    const fn new() -> Self {
605        Self {
606            slot_serializer: SlotSerializer::new(),
607            hash_serializer: HashSerializer::new(),
608        }
609    }
610}
611
612impl Default for BlockHeaderDenunciationSerializer {
613    fn default() -> Self {
614        Self::new()
615    }
616}
617
618impl Serializer<BlockHeaderDenunciation> for BlockHeaderDenunciationSerializer {
619    fn serialize(
620        &self,
621        value: &BlockHeaderDenunciation,
622        buffer: &mut Vec<u8>,
623    ) -> Result<(), SerializeError> {
624        buffer.extend(value.public_key.to_bytes());
625        self.slot_serializer.serialize(&value.slot, buffer)?;
626        self.hash_serializer.serialize(&value.hash_1, buffer)?;
627        self.hash_serializer.serialize(&value.hash_2, buffer)?;
628        buffer.extend(value.signature_1.to_bytes());
629        buffer.extend(value.signature_2.to_bytes());
630        Ok(())
631    }
632}
633
634/// Deserializer for `BlockHeaderDenunciation`
635struct BlockHeaderDenunciationDeserializer {
636    slot_deserializer: SlotDeserializer,
637    hash_deserializer: HashDeserializer,
638    pubkey_deserializer: PublicKeyDeserializer,
639    signature_deserializer: SignatureDeserializer,
640}
641
642impl BlockHeaderDenunciationDeserializer {
643    /// Creates a new `BlockHeaderDenunciationDeserializer`
644    pub const fn new(thread_count: u8) -> Self {
645        Self {
646            slot_deserializer: SlotDeserializer::new(
647                (Included(0), Included(u64::MAX)),
648                (Included(0), Excluded(thread_count)),
649            ),
650            hash_deserializer: HashDeserializer::new(),
651            pubkey_deserializer: PublicKeyDeserializer::new(),
652            signature_deserializer: SignatureDeserializer::new(),
653        }
654    }
655}
656
657impl Deserializer<BlockHeaderDenunciation> for BlockHeaderDenunciationDeserializer {
658    fn deserialize<'a, E: ParseError<&'a [u8]> + ContextError<&'a [u8]>>(
659        &self,
660        buffer: &'a [u8],
661    ) -> IResult<&'a [u8], BlockHeaderDenunciation, E> {
662        context(
663            "Failed BlockHeader Denunciation deserialization",
664            tuple((
665                context("Failed public key deserialization", |input| {
666                    self.pubkey_deserializer.deserialize(input)
667                }),
668                context("Failed slot deserialization", |input| {
669                    self.slot_deserializer.deserialize(input)
670                }),
671                context("Failed hash 1 deserialization", |input| {
672                    self.hash_deserializer.deserialize(input)
673                }),
674                context("Failed hash 2 deserialization", |input| {
675                    self.hash_deserializer.deserialize(input)
676                }),
677                context("Failed signature 1 deserialization", |input| {
678                    self.signature_deserializer.deserialize(input)
679                }),
680                context("Failed signature 2 deserialization", |input| {
681                    self.signature_deserializer.deserialize(input)
682                }),
683            )),
684        )
685        .map(
686            |(public_key, slot, hash_1, hash_2, signature_1, signature_2)| {
687                BlockHeaderDenunciation {
688                    public_key,
689                    slot,
690                    hash_1,
691                    hash_2,
692                    signature_1,
693                    signature_2,
694                }
695            },
696        )
697        .parse(buffer)
698    }
699}
700
701/// Serializer for `Denunciation`
702pub struct DenunciationSerializer {
703    endo_de_serializer: EndorsementDenunciationSerializer,
704    blkh_de_serializer: BlockHeaderDenunciationSerializer,
705    type_id_serializer: U32VarIntSerializer,
706}
707
708impl DenunciationSerializer {
709    /// Creates a new `BlockHeaderDenunciationSerializer`
710    pub const fn new() -> Self {
711        Self {
712            endo_de_serializer: EndorsementDenunciationSerializer::new(),
713            blkh_de_serializer: BlockHeaderDenunciationSerializer::new(),
714            type_id_serializer: U32VarIntSerializer::new(),
715        }
716    }
717}
718
719impl Default for DenunciationSerializer {
720    fn default() -> Self {
721        Self::new()
722    }
723}
724
725impl Serializer<Denunciation> for DenunciationSerializer {
726    fn serialize(&self, value: &Denunciation, buffer: &mut Vec<u8>) -> Result<(), SerializeError> {
727        let de_type_id = DenunciationTypeId::from(value);
728        self.type_id_serializer
729            .serialize(&u32::from(de_type_id), buffer)?;
730        match value {
731            Denunciation::Endorsement(de) => {
732                self.endo_de_serializer.serialize(de, buffer)?;
733            }
734            Denunciation::BlockHeader(de) => {
735                self.blkh_de_serializer.serialize(de, buffer)?;
736            }
737        }
738        Ok(())
739    }
740}
741
742/// Deserializer for `Denunciation`
743pub struct DenunciationDeserializer {
744    endo_de_deserializer: EndorsementDenunciationDeserializer,
745    blkh_de_deserializer: BlockHeaderDenunciationDeserializer,
746    type_id_deserializer: U32VarIntDeserializer,
747}
748
749impl DenunciationDeserializer {
750    /// Creates a new `DenunciationDeserializer`
751    pub const fn new(thread_count: u8, endorsement_count: u32) -> Self {
752        Self {
753            endo_de_deserializer: EndorsementDenunciationDeserializer::new(
754                thread_count,
755                endorsement_count,
756            ),
757            blkh_de_deserializer: BlockHeaderDenunciationDeserializer::new(thread_count),
758            type_id_deserializer: U32VarIntDeserializer::new(
759                Included(0),
760                Excluded(DenunciationTypeId::VARIANT_COUNT as u32),
761            ),
762        }
763    }
764}
765
766impl Deserializer<Denunciation> for DenunciationDeserializer {
767    fn deserialize<'a, E: ParseError<&'a [u8]> + ContextError<&'a [u8]>>(
768        &self,
769        buffer: &'a [u8],
770    ) -> IResult<&'a [u8], Denunciation, E> {
771        let (rem, de_type_id_) = context("Failed Denunciation type id deserialization", |input| {
772            self.type_id_deserializer.deserialize(input)
773        })
774        .parse(buffer)?;
775
776        let de_type_id = DenunciationTypeId::try_from(de_type_id_).map_err(|_| {
777            nom::Err::Error(ParseError::from_error_kind(
778                buffer,
779                nom::error::ErrorKind::Fail,
780            ))
781        })?;
782
783        match de_type_id {
784            DenunciationTypeId::Endorsement => {
785                let (rem2, endo_de) = self.endo_de_deserializer.deserialize(rem)?;
786                IResult::Ok((rem2, Denunciation::Endorsement(endo_de)))
787            }
788            DenunciationTypeId::BlockHeader => {
789                let (rem2, blkh_de) = self.blkh_de_deserializer.deserialize(rem)?;
790                IResult::Ok((rem2, Denunciation::BlockHeader(blkh_de)))
791            }
792        }
793    }
794}
795
796// End Ser / Der
797
798// Denunciation Index
799
800#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
801/// Index for Denunciations in collections (e.g. like a HashMap...)
802pub enum DenunciationIndex {
803    /// Variant for Block header denunciation index
804    BlockHeader {
805        /// de slot
806        slot: Slot,
807    },
808    /// Variant for Endorsement denunciation index
809    Endorsement {
810        /// de slot
811        slot: Slot,
812        /// de index
813        index: u32,
814    },
815}
816
817impl DenunciationIndex {
818    /// Get field: slot
819    pub fn get_slot(&self) -> &Slot {
820        match self {
821            DenunciationIndex::BlockHeader { slot } => slot,
822            DenunciationIndex::Endorsement { slot, .. } => slot,
823        }
824    }
825
826    /// Get field: index (return None for a block header denunciation index)
827    pub fn get_index(&self) -> Option<&u32> {
828        match self {
829            DenunciationIndex::BlockHeader { .. } => None,
830            DenunciationIndex::Endorsement { slot: _, index } => Some(index),
831        }
832    }
833
834    /// Compute the hash
835    pub fn get_hash(&self) -> Hash {
836        let mut buffer = u32::from(DenunciationIndexTypeId::from(self))
837            .to_le_bytes()
838            .to_vec();
839        match self {
840            DenunciationIndex::BlockHeader { slot } => buffer.extend(slot.to_bytes_key()),
841            DenunciationIndex::Endorsement { slot, index } => {
842                buffer.extend(slot.to_bytes_key());
843                buffer.extend(index.to_le_bytes());
844            }
845        }
846        Hash::compute_from(&buffer)
847    }
848}
849
850/// Create a `DenunciationIndex` from a `Denunciation`
851impl From<&Denunciation> for DenunciationIndex {
852    fn from(value: &Denunciation) -> Self {
853        match value {
854            Denunciation::Endorsement(endo_de) => DenunciationIndex::Endorsement {
855                slot: endo_de.slot,
856                index: endo_de.index,
857            },
858            Denunciation::BlockHeader(blkh_de) => {
859                DenunciationIndex::BlockHeader { slot: blkh_de.slot }
860            }
861        }
862    }
863}
864
865/// Create a `DenunciationIndex` from a `DenunciationPrecursor`
866impl From<&DenunciationPrecursor> for DenunciationIndex {
867    fn from(value: &DenunciationPrecursor) -> Self {
868        match value {
869            DenunciationPrecursor::Endorsement(de_p) => DenunciationIndex::Endorsement {
870                slot: de_p.slot,
871                index: de_p.index,
872            },
873            DenunciationPrecursor::BlockHeader(de_p) => {
874                DenunciationIndex::BlockHeader { slot: de_p.slot }
875            }
876        }
877    }
878}
879
880impl Ord for DenunciationIndex {
881    fn cmp(&self, other: &Self) -> Ordering {
882        // key ends with type id so that we avoid prioritizing one type over the other
883        // when filling block headers (comparison of tuples is from left to right).
884        let self_key = (
885            self.get_slot(),
886            self.get_index().unwrap_or(&0),
887            u32::from(DenunciationIndexTypeId::from(self)),
888        );
889        let other_key = (
890            other.get_slot(),
891            other.get_index().unwrap_or(&0),
892            u32::from(DenunciationIndexTypeId::from(other)),
893        );
894        self_key.cmp(&other_key)
895    }
896}
897
898impl PartialOrd for DenunciationIndex {
899    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
900        Some(self.cmp(other))
901    }
902}
903
904// End Denunciation Index
905
906// Denunciation Index ser der
907
908#[derive(IntoPrimitive, Debug, Eq, PartialEq, TryFromPrimitive)]
909#[repr(u32)]
910enum DenunciationIndexTypeId {
911    BlockHeader = 0,
912    Endorsement = 1,
913}
914
915impl From<&DenunciationIndex> for DenunciationIndexTypeId {
916    fn from(value: &DenunciationIndex) -> Self {
917        match value {
918            DenunciationIndex::BlockHeader { .. } => DenunciationIndexTypeId::BlockHeader,
919            DenunciationIndex::Endorsement { .. } => DenunciationIndexTypeId::Endorsement,
920        }
921    }
922}
923
924#[derive(Clone)]
925/// Serializer for `DenunciationIndex`
926pub struct DenunciationIndexSerializer {
927    u32_serializer: U32VarIntSerializer,
928    slot_serializer: SlotSerializer,
929    index_serializer: U32VarIntSerializer,
930}
931
932impl DenunciationIndexSerializer {
933    /// Creates a new `DenunciationIndexSerializer`
934    pub const fn new() -> Self {
935        Self {
936            u32_serializer: U32VarIntSerializer::new(),
937            slot_serializer: SlotSerializer::new(),
938            index_serializer: U32VarIntSerializer::new(),
939        }
940    }
941}
942
943impl Default for DenunciationIndexSerializer {
944    fn default() -> Self {
945        Self::new()
946    }
947}
948
949impl Serializer<DenunciationIndex> for DenunciationIndexSerializer {
950    fn serialize(
951        &self,
952        value: &DenunciationIndex,
953        buffer: &mut Vec<u8>,
954    ) -> Result<(), SerializeError> {
955        match value {
956            DenunciationIndex::BlockHeader { slot } => {
957                self.u32_serializer
958                    .serialize(&u32::from(DenunciationIndexTypeId::BlockHeader), buffer)?;
959                self.slot_serializer.serialize(slot, buffer)?;
960            }
961            DenunciationIndex::Endorsement { slot, index } => {
962                self.u32_serializer
963                    .serialize(&u32::from(DenunciationIndexTypeId::Endorsement), buffer)?;
964                self.slot_serializer.serialize(slot, buffer)?;
965                self.index_serializer.serialize(index, buffer)?;
966            }
967        }
968        Ok(())
969    }
970}
971
972#[derive(Clone)]
973/// Deserializer for `DenunciationIndex`
974pub struct DenunciationIndexDeserializer {
975    id_deserializer: U32VarIntDeserializer,
976    slot_deserializer: SlotDeserializer,
977    index_deserializer: U32VarIntDeserializer,
978}
979
980impl DenunciationIndexDeserializer {
981    /// Creates a new `DenunciationIndexDeserializer`
982    pub const fn new(thread_count: u8, endorsement_count: u32) -> Self {
983        Self {
984            id_deserializer: U32VarIntDeserializer::new(Included(0), Included(u32::MAX)),
985            slot_deserializer: SlotDeserializer::new(
986                (Included(0), Included(u64::MAX)),
987                (Included(0), Excluded(thread_count)),
988            ),
989            index_deserializer: U32VarIntDeserializer::new(
990                Included(0),
991                Included(endorsement_count),
992            ),
993        }
994    }
995}
996
997impl Deserializer<DenunciationIndex> for DenunciationIndexDeserializer {
998    fn deserialize<'a, E: ParseError<&'a [u8]> + ContextError<&'a [u8]>>(
999        &self,
1000        buffer: &'a [u8],
1001    ) -> IResult<&'a [u8], DenunciationIndex, E> {
1002        let (input, id) = self.id_deserializer.deserialize(buffer)?;
1003        let id = DenunciationIndexTypeId::try_from(id).map_err(|_| {
1004            nom::Err::Error(ParseError::from_error_kind(
1005                buffer,
1006                nom::error::ErrorKind::Eof,
1007            ))
1008        })?;
1009
1010        match id {
1011            DenunciationIndexTypeId::BlockHeader => {
1012                context("Failed slot deserialization", |input| {
1013                    self.slot_deserializer.deserialize(input)
1014                })
1015                .map(|slot| DenunciationIndex::BlockHeader { slot })
1016                // .parse(&buffer[1..])
1017                .parse(input)
1018            }
1019            DenunciationIndexTypeId::Endorsement => context(
1020                "Failed Endorsement denunciation index",
1021                tuple((
1022                    context("Failed slot deserialization", |input| {
1023                        self.slot_deserializer.deserialize(input)
1024                    }),
1025                    context("Failed index deserialization", |input| {
1026                        self.index_deserializer.deserialize(input)
1027                    }),
1028                )),
1029            )
1030            .map(|(slot, index)| DenunciationIndex::Endorsement { slot, index })
1031            .parse(input),
1032        }
1033    }
1034}
1035
1036// End Denunciation Index ser der
1037
1038// Denunciation interest
1039
1040/// DenunciationPrecursor variant for endorsement
1041#[derive(Debug, Clone, PartialEq)]
1042pub struct EndorsementDenunciationPrecursor {
1043    /// secure share endorsement public key
1044    pub public_key: PublicKey,
1045    /// endorsement slot
1046    pub slot: Slot,
1047    /// endorsement index
1048    pub index: u32,
1049    /// secured header partial hash
1050    hash: Hash,
1051    /// secured header signature
1052    signature: Signature,
1053}
1054
1055/// DenunciationPrecursor variant for block header
1056#[derive(Debug, Clone, PartialEq)]
1057pub struct BlockHeaderDenunciationPrecursor {
1058    /// secured header public key
1059    pub public_key: PublicKey,
1060    /// block header slot
1061    pub slot: Slot,
1062    /// secured header partial hash
1063    hash: Hash,
1064    /// secured header signature
1065    signature: Signature,
1066}
1067
1068/// Lightweight data for Denunciation creation
1069/// (avoid storing heavyweight secured header or secure share endorsement, see denunciation pool)
1070#[derive(Debug, Clone, PartialEq)]
1071pub enum DenunciationPrecursor {
1072    /// Endorsement variant
1073    Endorsement(EndorsementDenunciationPrecursor),
1074    /// Block header variant
1075    BlockHeader(BlockHeaderDenunciationPrecursor),
1076}
1077
1078impl DenunciationPrecursor {
1079    /// Get field: slot
1080    pub fn get_slot(&self) -> &Slot {
1081        match self {
1082            DenunciationPrecursor::Endorsement(endo_de_p) => &endo_de_p.slot,
1083            DenunciationPrecursor::BlockHeader(blkh_de_p) => &blkh_de_p.slot,
1084        }
1085    }
1086
1087    /// Get field: pub key
1088    pub fn get_public_key(&self) -> &PublicKey {
1089        match self {
1090            DenunciationPrecursor::Endorsement(endo_de_p) => &endo_de_p.public_key,
1091            DenunciationPrecursor::BlockHeader(blkh_de_p) => &blkh_de_p.public_key,
1092        }
1093    }
1094}
1095
1096impl From<&SecureShareEndorsement> for DenunciationPrecursor {
1097    fn from(value: &SecureShareEndorsement) -> Self {
1098        DenunciationPrecursor::Endorsement(EndorsementDenunciationPrecursor {
1099            public_key: value.content_creator_pub_key,
1100            slot: value.content.slot,
1101            index: value.content.index,
1102            hash: *value.id.get_hash(),
1103            signature: value.signature,
1104        })
1105    }
1106}
1107
1108impl From<&SecuredHeader> for DenunciationPrecursor {
1109    fn from(value: &SecuredHeader) -> Self {
1110        DenunciationPrecursor::BlockHeader(BlockHeaderDenunciationPrecursor {
1111            public_key: value.content_creator_pub_key,
1112            slot: value.content.slot,
1113            hash: *value.id.get_hash(),
1114            signature: value.signature,
1115        })
1116    }
1117}
1118
1119/// Create a new Denunciation from 2 SecureHeader
1120impl TryFrom<(&DenunciationPrecursor, &DenunciationPrecursor)> for Denunciation {
1121    type Error = DenunciationError;
1122
1123    fn try_from(
1124        (de_p_1, de_p_2): (&DenunciationPrecursor, &DenunciationPrecursor),
1125    ) -> Result<Self, Self::Error> {
1126        match (de_p_1, de_p_2) {
1127            (
1128                DenunciationPrecursor::BlockHeader(de_p_blkh_1),
1129                DenunciationPrecursor::BlockHeader(de_p_blkh_2),
1130            ) => {
1131                // Cannot use the same block header (here: block header denunciation precursor) twice
1132                if de_p_blkh_1.slot != de_p_blkh_2.slot
1133                    || de_p_blkh_1.public_key != de_p_blkh_2.public_key
1134                    || de_p_blkh_1.hash == de_p_blkh_2.hash
1135                {
1136                    return Err(DenunciationError::InvalidInput(
1137                        format!("Not the same slot or public key or same hash for de precursor: {:?} & {:?}", de_p_blkh_1, de_p_blkh_2)
1138                    ));
1139                }
1140
1141                // Check sig
1142                let de_p_blkh_1_hash = BlockHeaderDenunciation::compute_hash_for_sig_verif(
1143                    &de_p_blkh_1.public_key,
1144                    &de_p_blkh_1.slot,
1145                    &de_p_blkh_1.hash,
1146                );
1147                let de_p_blkh_2_hash = BlockHeaderDenunciation::compute_hash_for_sig_verif(
1148                    &de_p_blkh_2.public_key,
1149                    &de_p_blkh_2.slot,
1150                    &de_p_blkh_2.hash,
1151                );
1152
1153                de_p_blkh_1
1154                    .public_key
1155                    .verify_signature(&de_p_blkh_1_hash, &de_p_blkh_1.signature)?;
1156                de_p_blkh_1
1157                    .public_key
1158                    .verify_signature(&de_p_blkh_2_hash, &de_p_blkh_2.signature)?;
1159
1160                Ok(Denunciation::BlockHeader(BlockHeaderDenunciation {
1161                    public_key: de_p_blkh_1.public_key,
1162                    slot: de_p_blkh_1.slot,
1163                    signature_1: de_p_blkh_1.signature,
1164                    signature_2: de_p_blkh_2.signature,
1165                    hash_1: de_p_blkh_1.hash,
1166                    hash_2: de_p_blkh_2.hash,
1167                }))
1168            }
1169            (
1170                DenunciationPrecursor::Endorsement(de_p_endo_1),
1171                DenunciationPrecursor::Endorsement(de_p_endo_2),
1172            ) => {
1173                // Cannot use the same endorsement (here: endorsement denunciation) twice
1174                if de_p_endo_1.slot != de_p_endo_2.slot
1175                    || de_p_endo_1.index != de_p_endo_2.index
1176                    || de_p_endo_1.public_key != de_p_endo_2.public_key
1177                    || de_p_endo_1.hash == de_p_endo_2.hash
1178                {
1179                    return Err(DenunciationError::InvalidInput(
1180                        format!("Not the same slot, index or public key or same hash for de precursor: {:?} & {:?}", de_p_endo_1, de_p_endo_2)
1181                    ));
1182                }
1183
1184                // Check sig
1185                let de_p_endo_1_hash = EndorsementDenunciation::compute_hash_for_sig_verif(
1186                    &de_p_endo_1.public_key,
1187                    &de_p_endo_1.slot,
1188                    &de_p_endo_1.index,
1189                    &de_p_endo_1.hash,
1190                );
1191                let de_p_endo_2_hash = EndorsementDenunciation::compute_hash_for_sig_verif(
1192                    &de_p_endo_2.public_key,
1193                    &de_p_endo_2.slot,
1194                    &de_p_endo_2.index,
1195                    &de_p_endo_2.hash,
1196                );
1197
1198                de_p_endo_1
1199                    .public_key
1200                    .verify_signature(&de_p_endo_1_hash, &de_p_endo_1.signature)?;
1201                de_p_endo_1
1202                    .public_key
1203                    .verify_signature(&de_p_endo_2_hash, &de_p_endo_2.signature)?;
1204
1205                Ok(Denunciation::Endorsement(EndorsementDenunciation {
1206                    public_key: de_p_endo_1.public_key,
1207                    slot: de_p_endo_1.slot,
1208                    index: de_p_endo_1.index,
1209                    signature_1: de_p_endo_1.signature,
1210                    signature_2: de_p_endo_2.signature,
1211                    hash_1: de_p_endo_1.hash,
1212                    hash_2: de_p_endo_2.hash,
1213                }))
1214            }
1215            _ => {
1216                // Different enum variants - this is invalid
1217                Err(DenunciationError::InvalidInput(
1218                    "Mixed endorsement and block header denunciation precursors".to_string(),
1219                ))
1220            }
1221        }
1222    }
1223}
1224
1225// End Denunciation interest
1226
1227// test-exports
1228
1229#[cfg(any(test, feature = "test-exports"))]
1230impl Denunciation {
1231    /// Used under testing conditions to validate an instance of Self
1232    pub fn check_invariants(&self) -> Result<(), Box<dyn std::error::Error>> {
1233        if !self.is_valid() {
1234            return Err(format!("Denunciation is invalid: {:?}", self).into());
1235        }
1236        Ok(())
1237    }
1238}
1239
1240// end test-exports
1241
1242#[cfg(test)]
1243mod tests {
1244    use super::*;
1245
1246    use massa_serialization::DeserializeError;
1247    use massa_signature::KeyPair;
1248
1249    use crate::block_id::BlockId;
1250    use crate::config::{CHAINID, ENDORSEMENT_COUNT, THREAD_COUNT};
1251    use crate::endorsement::{Endorsement, EndorsementSerializer, SecureShareEndorsement};
1252    use crate::secure_share::{Id, SecureShareContent};
1253
1254    use crate::test_exports::{
1255        gen_block_headers_for_denunciation, gen_endorsements_for_denunciation,
1256    };
1257
1258    #[test]
1259    fn test_endorsement_denunciation() {
1260        // Create an endorsement denunciation and check if it is valid
1261        let (_slot, _keypair, s_endorsement_1, s_endorsement_2, _s_endorsement_3) =
1262            gen_endorsements_for_denunciation(None, None);
1263        let denunciation: Denunciation = (&s_endorsement_1, &s_endorsement_2).try_into().unwrap();
1264
1265        assert!(denunciation.is_for_endorsement());
1266        assert!(denunciation.is_valid());
1267    }
1268
1269    #[test]
1270    fn test_endorsement_denunciation_invalid_1() {
1271        let (slot, keypair, s_endorsement_1, _s_endorsement_2, _s_endorsement_3) =
1272            gen_endorsements_for_denunciation(None, None);
1273
1274        // Try to create a denunciation from 2 endorsements @ != index
1275        let endorsement_4 = Endorsement {
1276            slot,
1277            index: 9,
1278            endorsed_block: BlockId::generate_from_hash(Hash::compute_from("foo".as_bytes())),
1279        };
1280        let s_endorsement_4 = Endorsement::new_verifiable(
1281            endorsement_4,
1282            EndorsementSerializer::new(),
1283            &keypair,
1284            *CHAINID,
1285        )
1286        .unwrap();
1287
1288        let denunciation = Denunciation::try_from((&s_endorsement_1, &s_endorsement_4));
1289
1290        assert!(matches!(
1291            denunciation,
1292            Err(DenunciationError::InvalidInput(..))
1293        ));
1294
1295        // Try to create a denunciation from only 1 endorsement
1296        let denunciation = Denunciation::try_from((&s_endorsement_1, &s_endorsement_1));
1297
1298        assert!(matches!(
1299            denunciation,
1300            Err(DenunciationError::InvalidInput(..))
1301        ));
1302    }
1303
1304    #[test]
1305    fn test_endorsement_denunciation_is_for() {
1306        let (slot, keypair, s_endorsement_1, s_endorsement_2, s_endorsement_3) =
1307            gen_endorsements_for_denunciation(None, None);
1308
1309        let denunciation: Denunciation = (&s_endorsement_1, &s_endorsement_2).try_into().unwrap();
1310
1311        assert!(denunciation.is_for_endorsement());
1312        assert!(denunciation.is_valid());
1313
1314        // Try to create a denunciation from 2 endorsements @ != index
1315        let endorsement_4 = Endorsement {
1316            slot,
1317            index: 9,
1318            endorsed_block: BlockId::generate_from_hash(Hash::compute_from("foo".as_bytes())),
1319        };
1320        let s_endorsement_4 = Endorsement::new_verifiable(
1321            endorsement_4,
1322            EndorsementSerializer::new(),
1323            &keypair,
1324            *CHAINID,
1325        )
1326        .unwrap();
1327
1328        assert!(!denunciation
1329            .is_also_for_endorsement(&s_endorsement_4)
1330            .unwrap());
1331        assert!(denunciation
1332            .is_also_for_endorsement(&s_endorsement_3)
1333            .unwrap());
1334        assert!(denunciation.is_valid());
1335    }
1336
1337    #[test]
1338    fn test_block_header_denunciation() {
1339        // Create an block header denunciation and check if it is valid
1340        let (_slot, _keypair, s_block_header_1, s_block_header_2, s_block_header_3) =
1341            gen_block_headers_for_denunciation(None, None);
1342        let denunciation: Denunciation = (&s_block_header_1, &s_block_header_2).try_into().unwrap();
1343
1344        assert!(denunciation.is_for_block_header());
1345        assert!(denunciation.is_valid());
1346        assert!(denunciation
1347            .is_also_for_block_header(&s_block_header_3)
1348            .unwrap());
1349    }
1350
1351    #[test]
1352    fn test_forge_invalid_denunciation() {
1353        let keypair = KeyPair::generate(0).unwrap();
1354        let slot_1 = Slot::new(4, 2);
1355        let slot_2 = Slot::new(3, 7);
1356
1357        let endorsement_1 = Endorsement {
1358            slot: slot_1,
1359            index: 0,
1360            endorsed_block: BlockId::generate_from_hash(Hash::compute_from("blk1".as_bytes())),
1361        };
1362
1363        let s_endorsement_1: SecureShareEndorsement = Endorsement::new_verifiable(
1364            endorsement_1,
1365            EndorsementSerializer::new(),
1366            &keypair,
1367            *CHAINID,
1368        )
1369        .unwrap();
1370
1371        let endorsement_2 = Endorsement {
1372            slot: slot_2,
1373            index: 0,
1374            endorsed_block: BlockId::generate_from_hash(Hash::compute_from("blk2".as_bytes())),
1375        };
1376
1377        let s_endorsement_2: SecureShareEndorsement = Endorsement::new_verifiable(
1378            endorsement_2,
1379            EndorsementSerializer::new(),
1380            &keypair,
1381            *CHAINID,
1382        )
1383        .unwrap();
1384
1385        // from an attacker - building manually a Denunciation object
1386        let de_forged_1 = Denunciation::Endorsement(EndorsementDenunciation {
1387            public_key: keypair.get_public_key(),
1388            slot: slot_1,
1389            index: 0,
1390            hash_1: *s_endorsement_1.id.get_hash(), // use only data from s_endorsement_1
1391            hash_2: *s_endorsement_1.id.get_hash(),
1392            signature_1: s_endorsement_1.signature,
1393            signature_2: s_endorsement_1.signature,
1394        });
1395
1396        // hash_1 == hash_2 -> this is invalid
1397        assert!(!de_forged_1.is_valid());
1398
1399        // from an attacker - building manually a Denunciation object
1400        let de_forged_2 = Denunciation::Endorsement(EndorsementDenunciation {
1401            public_key: keypair.get_public_key(),
1402            slot: slot_2,
1403            index: 0,
1404            hash_1: *s_endorsement_1.id.get_hash(),
1405            hash_2: *s_endorsement_2.id.get_hash(),
1406            signature_1: s_endorsement_1.signature,
1407            signature_2: s_endorsement_2.signature,
1408        });
1409
1410        // An attacker uses an old s_endorsement_1 to forge a Denunciation object @ slot_2
1411        // This has to be detected if Denunciation are send via the network
1412        assert!(!de_forged_2.is_valid());
1413    }
1414
1415    // SER / DER
1416    #[test]
1417    fn test_endorsement_denunciation_ser_der() {
1418        let (_, _, s_endorsement_1, s_endorsement_2, _) =
1419            gen_endorsements_for_denunciation(None, None);
1420
1421        let denunciation = Denunciation::try_from((&s_endorsement_1, &s_endorsement_2)).unwrap();
1422
1423        let mut buffer = Vec::new();
1424        let de_ser = EndorsementDenunciationSerializer::new();
1425
1426        match denunciation {
1427            Denunciation::Endorsement(de) => {
1428                de_ser.serialize(&de, &mut buffer).unwrap();
1429                let de_der =
1430                    EndorsementDenunciationDeserializer::new(THREAD_COUNT, ENDORSEMENT_COUNT);
1431
1432                let (rem, de_der_res) = de_der.deserialize::<DeserializeError>(&buffer).unwrap();
1433
1434                assert!(rem.is_empty());
1435                assert_eq!(de, de_der_res);
1436            }
1437            Denunciation::BlockHeader(_) => {
1438                unimplemented!()
1439            }
1440        }
1441    }
1442
1443    #[test]
1444    fn test_block_header_denunciation_ser_der() {
1445        let (_, _, s_block_header_1, s_block_header_2, _) =
1446            gen_block_headers_for_denunciation(None, None);
1447        let denunciation: Denunciation = (&s_block_header_1, &s_block_header_2).try_into().unwrap();
1448
1449        let mut buffer = Vec::new();
1450        let de_ser = BlockHeaderDenunciationSerializer::new();
1451
1452        match denunciation {
1453            Denunciation::Endorsement(_) => {
1454                unimplemented!()
1455            }
1456            Denunciation::BlockHeader(de) => {
1457                de_ser.serialize(&de, &mut buffer).unwrap();
1458                let de_der = BlockHeaderDenunciationDeserializer::new(THREAD_COUNT);
1459
1460                let (rem, de_der_res) = de_der.deserialize::<DeserializeError>(&buffer).unwrap();
1461
1462                assert!(rem.is_empty());
1463                assert_eq!(de, de_der_res);
1464            }
1465        }
1466    }
1467
1468    #[test]
1469    fn test_denunciation_ser_der() {
1470        let (_, _, s_block_header_1, s_block_header_2, _) =
1471            gen_block_headers_for_denunciation(None, None);
1472        let denunciation: Denunciation = (&s_block_header_1, &s_block_header_2).try_into().unwrap();
1473
1474        let mut buffer = Vec::new();
1475        let de_ser = DenunciationSerializer::new();
1476
1477        de_ser.serialize(&denunciation, &mut buffer).unwrap();
1478        let de_der = DenunciationDeserializer::new(THREAD_COUNT, ENDORSEMENT_COUNT);
1479
1480        let (rem, de_der_res) = de_der.deserialize::<DeserializeError>(&buffer).unwrap();
1481
1482        assert!(rem.is_empty());
1483        assert_eq!(denunciation, de_der_res);
1484
1485        let (_, _, s_endorsement_1, s_endorsement_2, _) =
1486            gen_endorsements_for_denunciation(None, None);
1487        let denunciation = Denunciation::try_from((&s_endorsement_1, &s_endorsement_2)).unwrap();
1488        buffer.clear();
1489
1490        de_ser.serialize(&denunciation, &mut buffer).unwrap();
1491        let (rem, de_der_res) = de_der.deserialize::<DeserializeError>(&buffer).unwrap();
1492        assert!(rem.is_empty());
1493        assert_eq!(denunciation, de_der_res);
1494    }
1495
1496    #[test]
1497    fn test_denunciation_precursor() {
1498        let (_, _, s_block_header_1, s_block_header_2, _) =
1499            gen_block_headers_for_denunciation(None, None);
1500        let denunciation: Denunciation = (&s_block_header_1, &s_block_header_2).try_into().unwrap();
1501
1502        let de_p_1 = DenunciationPrecursor::from(&s_block_header_1);
1503        let de_p_2 = DenunciationPrecursor::from(&s_block_header_2);
1504        let denunciation_2: Denunciation = (&de_p_1, &de_p_2).try_into().unwrap();
1505
1506        assert_eq!(denunciation, denunciation_2);
1507
1508        let (_, _, s_endorsement_1, s_endorsement_2, _) =
1509            gen_endorsements_for_denunciation(None, None);
1510        let denunciation_3 = Denunciation::try_from((&s_endorsement_1, &s_endorsement_2)).unwrap();
1511
1512        let de_p_3 = DenunciationPrecursor::from(&s_endorsement_1);
1513        let de_p_4 = DenunciationPrecursor::from(&s_endorsement_2);
1514        let denunciation_4: Denunciation = (&de_p_3, &de_p_4).try_into().unwrap();
1515
1516        assert_eq!(denunciation_3, denunciation_4);
1517    }
1518
1519    #[test]
1520    fn test_denunciation_index_ser_der() {
1521        let (_, _, s_block_header_1, s_block_header_2, _) =
1522            gen_block_headers_for_denunciation(None, None);
1523        let denunciation_1: Denunciation =
1524            (&s_block_header_1, &s_block_header_2).try_into().unwrap();
1525        let denunciation_index_1 = DenunciationIndex::from(&denunciation_1);
1526
1527        let (_, _, s_endorsement_1, s_endorsement_2, _) =
1528            gen_endorsements_for_denunciation(None, None);
1529        let denunciation_2 = Denunciation::try_from((&s_endorsement_1, &s_endorsement_2)).unwrap();
1530        let denunciation_index_2 = DenunciationIndex::from(&denunciation_2);
1531
1532        let mut buffer = Vec::new();
1533        let de_idx_ser = DenunciationIndexSerializer::new();
1534        de_idx_ser
1535            .serialize(&denunciation_index_1, &mut buffer)
1536            .unwrap();
1537        let de_idx_der = DenunciationIndexDeserializer::new(THREAD_COUNT, ENDORSEMENT_COUNT);
1538        let (rem, de_idx_der_res) = de_idx_der.deserialize::<DeserializeError>(&buffer).unwrap();
1539
1540        assert!(rem.is_empty());
1541        assert_eq!(denunciation_index_1, de_idx_der_res);
1542
1543        let mut buffer = Vec::new();
1544        let de_idx_ser = DenunciationIndexSerializer::new();
1545        de_idx_ser
1546            .serialize(&denunciation_index_2, &mut buffer)
1547            .unwrap();
1548        let de_idx_der = DenunciationIndexDeserializer::new(THREAD_COUNT, ENDORSEMENT_COUNT);
1549        let (rem, de_idx_der_res) = de_idx_der.deserialize::<DeserializeError>(&buffer).unwrap();
1550
1551        assert!(rem.is_empty());
1552        assert_eq!(denunciation_index_2, de_idx_der_res);
1553    }
1554}