1#![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#[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 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 hash_data.extend(public_key.to_bytes());
94 let denunciation_data = EndorsementDenunciationData::new(*slot, *index);
96 hash_data.extend(&denunciation_data.to_bytes());
97 hash_data.extend(content_hash.to_bytes());
99 Hash::compute_from(&hash_data)
100 }
101
102 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#[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 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 hash_data.extend(public_key.to_bytes());
148 let de_data = BlockHeaderDenunciationData::new(*slot);
150 hash_data.extend(de_data.to_bytes());
151 hash_data.extend(content_hash.to_bytes());
153 Hash::compute_from(&hash_data)
154 }
155
156 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#[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 pub fn is_for_endorsement(&self) -> bool {
189 matches!(self, Denunciation::Endorsement(_))
190 }
191
192 pub fn is_for_block_header(&self) -> bool {
194 matches!(self, Denunciation::BlockHeader(_))
195 }
196
197 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 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 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 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 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 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 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
345impl 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 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 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 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
399impl 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 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 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#[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
477struct EndorsementDenunciationSerializer {
481 slot_serializer: SlotSerializer,
482 u32_serializer: U32VarIntSerializer,
483 hash_serializer: HashSerializer,
484}
485
486impl EndorsementDenunciationSerializer {
487 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
520struct 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 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
596struct BlockHeaderDenunciationSerializer {
598 slot_serializer: SlotSerializer,
599 hash_serializer: HashSerializer,
600}
601
602impl BlockHeaderDenunciationSerializer {
603 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
634struct BlockHeaderDenunciationDeserializer {
636 slot_deserializer: SlotDeserializer,
637 hash_deserializer: HashDeserializer,
638 pubkey_deserializer: PublicKeyDeserializer,
639 signature_deserializer: SignatureDeserializer,
640}
641
642impl BlockHeaderDenunciationDeserializer {
643 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
701pub struct DenunciationSerializer {
703 endo_de_serializer: EndorsementDenunciationSerializer,
704 blkh_de_serializer: BlockHeaderDenunciationSerializer,
705 type_id_serializer: U32VarIntSerializer,
706}
707
708impl DenunciationSerializer {
709 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
742pub struct DenunciationDeserializer {
744 endo_de_deserializer: EndorsementDenunciationDeserializer,
745 blkh_de_deserializer: BlockHeaderDenunciationDeserializer,
746 type_id_deserializer: U32VarIntDeserializer,
747}
748
749impl DenunciationDeserializer {
750 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#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
801pub enum DenunciationIndex {
803 BlockHeader {
805 slot: Slot,
807 },
808 Endorsement {
810 slot: Slot,
812 index: u32,
814 },
815}
816
817impl DenunciationIndex {
818 pub fn get_slot(&self) -> &Slot {
820 match self {
821 DenunciationIndex::BlockHeader { slot } => slot,
822 DenunciationIndex::Endorsement { slot, .. } => slot,
823 }
824 }
825
826 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 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
850impl 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
865impl 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 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#[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)]
925pub struct DenunciationIndexSerializer {
927 u32_serializer: U32VarIntSerializer,
928 slot_serializer: SlotSerializer,
929 index_serializer: U32VarIntSerializer,
930}
931
932impl DenunciationIndexSerializer {
933 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)]
973pub struct DenunciationIndexDeserializer {
975 id_deserializer: U32VarIntDeserializer,
976 slot_deserializer: SlotDeserializer,
977 index_deserializer: U32VarIntDeserializer,
978}
979
980impl DenunciationIndexDeserializer {
981 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(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#[derive(Debug, Clone, PartialEq)]
1042pub struct EndorsementDenunciationPrecursor {
1043 pub public_key: PublicKey,
1045 pub slot: Slot,
1047 pub index: u32,
1049 hash: Hash,
1051 signature: Signature,
1053}
1054
1055#[derive(Debug, Clone, PartialEq)]
1057pub struct BlockHeaderDenunciationPrecursor {
1058 pub public_key: PublicKey,
1060 pub slot: Slot,
1062 hash: Hash,
1064 signature: Signature,
1066}
1067
1068#[derive(Debug, Clone, PartialEq)]
1071pub enum DenunciationPrecursor {
1072 Endorsement(EndorsementDenunciationPrecursor),
1074 BlockHeader(BlockHeaderDenunciationPrecursor),
1076}
1077
1078impl DenunciationPrecursor {
1079 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 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
1119impl 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 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 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 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 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 Err(DenunciationError::InvalidInput(
1218 "Mixed endorsement and block header denunciation precursors".to_string(),
1219 ))
1220 }
1221 }
1222 }
1223}
1224
1225#[cfg(any(test, feature = "test-exports"))]
1230impl Denunciation {
1231 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#[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 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 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 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 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 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 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(), 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 assert!(!de_forged_1.is_valid());
1398
1399 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 assert!(!de_forged_2.is_valid());
1413 }
1414
1415 #[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}