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(
91 public_key: &PublicKey,
92 slot: &Slot,
93 index: &u32,
94 content_hash: &Hash,
95 sig_chain_id: Option<u64>,
96 ) -> Hash {
97 let mut hash_data = Vec::new();
98 hash_data.extend(public_key.to_bytes());
100 let denunciation_data = EndorsementDenunciationData::new(*slot, *index, sig_chain_id);
102 hash_data.extend(&denunciation_data.to_bytes());
103 hash_data.extend(content_hash.to_bytes());
105 Hash::compute_from(&hash_data)
106 }
107
108 pub fn get_public_key(&self) -> &PublicKey {
110 &self.public_key
111 }
112 pub fn get_slot(&self) -> &Slot {
113 &self.slot
114 }
115 pub fn get_index(&self) -> &u32 {
116 &self.index
117 }
118 pub fn get_hash_1(&self) -> &Hash {
119 &self.hash_1
120 }
121 pub fn get_hash_2(&self) -> &Hash {
122 &self.hash_2
123 }
124 pub fn get_signature_1(&self) -> &Signature {
125 &self.signature_1
126 }
127 pub fn get_signature_2(&self) -> &Signature {
128 &self.signature_2
129 }
130}
131
132#[allow(dead_code)]
134#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
135pub struct BlockHeaderDenunciation {
136 public_key: PublicKey,
137 slot: Slot,
138 hash_1: Hash,
139 hash_2: Hash,
140 signature_1: Signature,
141 signature_2: Signature,
142}
143
144impl BlockHeaderDenunciation {
145 fn compute_hash_for_sig_verif(
152 public_key: &PublicKey,
153 slot: &Slot,
154 content_hash: &Hash,
155 sig_chain_id: Option<u64>,
156 ) -> Hash {
157 let mut hash_data = Vec::new();
158 hash_data.extend(public_key.to_bytes());
160 let de_data = BlockHeaderDenunciationData::new(*slot, sig_chain_id);
162 hash_data.extend(de_data.to_bytes());
163 hash_data.extend(content_hash.to_bytes());
165 Hash::compute_from(&hash_data)
166 }
167
168 pub fn get_public_key(&self) -> &PublicKey {
170 &self.public_key
171 }
172 pub fn get_slot(&self) -> &Slot {
173 &self.slot
174 }
175 pub fn get_hash_1(&self) -> &Hash {
176 &self.hash_1
177 }
178 pub fn get_hash_2(&self) -> &Hash {
179 &self.hash_2
180 }
181 pub fn get_signature_1(&self) -> &Signature {
182 &self.signature_1
183 }
184 pub fn get_signature_2(&self) -> &Signature {
185 &self.signature_2
186 }
187}
188
189#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
191#[allow(missing_docs)]
192pub enum Denunciation {
193 Endorsement(EndorsementDenunciation),
194 BlockHeader(BlockHeaderDenunciation),
195}
196
197#[allow(dead_code)]
198impl Denunciation {
199 pub fn is_for_endorsement(&self) -> bool {
201 matches!(self, Denunciation::Endorsement(_))
202 }
203
204 pub fn is_for_block_header(&self) -> bool {
206 matches!(self, Denunciation::BlockHeader(_))
207 }
208
209 pub fn is_also_for_endorsement(
214 &self,
215 s_endorsement: &SecureShareEndorsement,
216 sig_chain_id: Option<u64>,
217 ) -> Result<bool, DenunciationError> {
218 match self {
219 Denunciation::BlockHeader(_) => Ok(false),
220 Denunciation::Endorsement(endo_de) => {
221 let content_hash = s_endorsement.id.get_hash();
222
223 let hash = EndorsementDenunciation::compute_hash_for_sig_verif(
224 &endo_de.public_key,
225 &endo_de.slot,
226 &endo_de.index,
227 content_hash,
228 sig_chain_id,
229 );
230
231 Ok(endo_de.slot == s_endorsement.content.slot
232 && endo_de.index == s_endorsement.content.index
233 && endo_de.public_key == s_endorsement.content_creator_pub_key
234 && endo_de.hash_1 != *content_hash
235 && endo_de.hash_2 != *content_hash
236 && endo_de
237 .public_key
238 .verify_signature(&hash, &s_endorsement.signature)
239 .is_ok())
240 }
241 }
242 }
243
244 pub fn is_also_for_block_header(
249 &self,
250 s_block_header: &SecuredHeader,
251 sig_chain_id: Option<u64>,
252 ) -> Result<bool, DenunciationError> {
253 match self {
254 Denunciation::Endorsement(_) => Ok(false),
255 Denunciation::BlockHeader(endo_bh) => {
256 let content_hash = s_block_header.id.get_hash();
257
258 let hash = BlockHeaderDenunciation::compute_hash_for_sig_verif(
259 &endo_bh.public_key,
260 &endo_bh.slot,
261 content_hash,
262 sig_chain_id,
263 );
264
265 Ok(endo_bh.slot == s_block_header.content.slot
266 && endo_bh.public_key == s_block_header.content_creator_pub_key
267 && endo_bh.hash_1 != *content_hash
268 && endo_bh.hash_2 != *content_hash
269 && endo_bh
270 .public_key
271 .verify_signature(&hash, &s_block_header.signature)
272 .is_ok())
273 }
274 }
275 }
276
277 pub fn is_valid(&self) -> bool {
283 self.is_valid_with_chain(None)
284 }
285
286 pub fn is_valid_with_chain(&self, sig_chain_id: Option<u64>) -> bool {
296 let (signature_1, signature_2, hash_1, hash_2, public_key) = match self {
297 Denunciation::Endorsement(de) => {
298 let hash_1 = EndorsementDenunciation::compute_hash_for_sig_verif(
299 &de.public_key,
300 &de.slot,
301 &de.index,
302 &de.hash_1,
303 sig_chain_id,
304 );
305 let hash_2 = EndorsementDenunciation::compute_hash_for_sig_verif(
306 &de.public_key,
307 &de.slot,
308 &de.index,
309 &de.hash_2,
310 sig_chain_id,
311 );
312
313 (
314 de.signature_1,
315 de.signature_2,
316 hash_1,
317 hash_2,
318 de.public_key,
319 )
320 }
321 Denunciation::BlockHeader(de) => {
322 let hash_1 = BlockHeaderDenunciation::compute_hash_for_sig_verif(
323 &de.public_key,
324 &de.slot,
325 &de.hash_1,
326 sig_chain_id,
327 );
328 let hash_2 = BlockHeaderDenunciation::compute_hash_for_sig_verif(
329 &de.public_key,
330 &de.slot,
331 &de.hash_2,
332 sig_chain_id,
333 );
334
335 (
336 de.signature_1,
337 de.signature_2,
338 hash_1,
339 hash_2,
340 de.public_key,
341 )
342 }
343 };
344
345 hash_1 != hash_2
346 && public_key.verify_signature(&hash_1, &signature_1).is_ok()
347 && public_key.verify_signature(&hash_2, &signature_2).is_ok()
348 }
349
350 pub fn get_slot(&self) -> &Slot {
352 match self {
353 Denunciation::Endorsement(de) => &de.slot,
354 Denunciation::BlockHeader(de) => &de.slot,
355 }
356 }
357
358 pub fn get_index(&self) -> Option<&u32> {
360 match self {
361 Denunciation::BlockHeader(_) => None,
362 Denunciation::Endorsement(de) => Some(&de.index),
363 }
364 }
365
366 pub fn get_public_key(&self) -> &PublicKey {
368 match self {
369 Denunciation::Endorsement(de) => &de.public_key,
370 Denunciation::BlockHeader(de) => &de.public_key,
371 }
372 }
373
374 pub fn is_expired(
379 denunciation_slot_period: &u64,
380 slot_period: &u64,
381 denunciation_expire_periods: &u64,
382 ) -> bool {
383 slot_period.checked_sub(*denunciation_slot_period) > Some(*denunciation_expire_periods)
384 }
385}
386
387impl TryFrom<(&SecureShareEndorsement, &SecureShareEndorsement)> for Denunciation {
391 type Error = DenunciationError;
392
393 fn try_from(
394 (s_e1, s_e2): (&SecureShareEndorsement, &SecureShareEndorsement),
395 ) -> Result<Self, Self::Error> {
396 Denunciation::try_from((s_e1, s_e2, None))
397 }
398}
399
400impl
405 TryFrom<(
406 &SecureShareEndorsement,
407 &SecureShareEndorsement,
408 Option<u64>,
409 )> for Denunciation
410{
411 type Error = DenunciationError;
412
413 fn try_from(
414 (s_e1, s_e2, sig_chain_id): (
415 &SecureShareEndorsement,
416 &SecureShareEndorsement,
417 Option<u64>,
418 ),
419 ) -> Result<Self, Self::Error> {
420 if s_e1.content.slot != s_e2.content.slot
423 || s_e1.content.index != s_e2.content.index
424 || s_e1.content_creator_pub_key != s_e2.content_creator_pub_key
425 || s_e1.id == s_e2.id
426 {
427 return Err(DenunciationError::InvalidInput(format!(
428 "Not the same slot, index or public key or same hash for {:?} & {:?}",
429 s_e1, s_e2
430 )));
431 }
432
433 let s_e1_hash_content = s_e1.id.get_hash();
435 let s_e1_hash = EndorsementDenunciation::compute_hash_for_sig_verif(
436 &s_e1.content_creator_pub_key,
437 &s_e1.content.slot,
438 &s_e1.content.index,
439 s_e1_hash_content,
440 sig_chain_id,
441 );
442 let s_e2_hash_content = s_e2.id.get_hash();
444 let s_e2_hash = EndorsementDenunciation::compute_hash_for_sig_verif(
445 &s_e1.content_creator_pub_key,
446 &s_e1.content.slot,
447 &s_e1.content.index,
448 s_e2_hash_content,
449 sig_chain_id,
450 );
451
452 s_e1.content_creator_pub_key
453 .verify_signature(&s_e1_hash, &s_e1.signature)?;
454 s_e1.content_creator_pub_key
455 .verify_signature(&s_e2_hash, &s_e2.signature)?;
456
457 Ok(Denunciation::Endorsement(EndorsementDenunciation {
458 public_key: s_e1.content_creator_pub_key,
459 slot: s_e1.content.slot,
460 index: s_e1.content.index,
461 signature_1: s_e1.signature,
462 signature_2: s_e2.signature,
463 hash_1: *s_e1_hash_content,
464 hash_2: *s_e2_hash_content,
465 }))
466 }
467}
468
469impl TryFrom<(&SecuredHeader, &SecuredHeader)> for Denunciation {
473 type Error = DenunciationError;
474
475 fn try_from((s_bh1, s_bh2): (&SecuredHeader, &SecuredHeader)) -> Result<Self, Self::Error> {
476 Denunciation::try_from((s_bh1, s_bh2, None))
477 }
478}
479
480impl TryFrom<(&SecuredHeader, &SecuredHeader, Option<u64>)> for Denunciation {
484 type Error = DenunciationError;
485
486 fn try_from(
487 (s_bh1, s_bh2, sig_chain_id): (&SecuredHeader, &SecuredHeader, Option<u64>),
488 ) -> Result<Self, Self::Error> {
489 if s_bh1.content.slot != s_bh2.content.slot
492 || s_bh1.content_creator_pub_key != s_bh2.content_creator_pub_key
493 || s_bh1.id == s_bh2.id
494 {
495 return Err(DenunciationError::InvalidInput(format!(
496 "Not the same slot or public key or same hash for {:?} & {:?}",
497 s_bh1, s_bh2
498 )));
499 }
500
501 let s_bh1_hash_content = s_bh1.id.get_hash();
503 let s_bh1_hash = BlockHeaderDenunciation::compute_hash_for_sig_verif(
504 &s_bh1.content_creator_pub_key,
505 &s_bh1.content.slot,
506 s_bh1_hash_content,
507 sig_chain_id,
508 );
509 let s_bh2_hash_content = s_bh2.id.get_hash();
510 let s_bh2_hash = BlockHeaderDenunciation::compute_hash_for_sig_verif(
511 &s_bh1.content_creator_pub_key,
512 &s_bh1.content.slot,
513 s_bh2_hash_content,
514 sig_chain_id,
515 );
516
517 s_bh1
518 .content_creator_pub_key
519 .verify_signature(&s_bh1_hash, &s_bh1.signature)?;
520 s_bh1
521 .content_creator_pub_key
522 .verify_signature(&s_bh2_hash, &s_bh2.signature)?;
523
524 Ok(Denunciation::BlockHeader(BlockHeaderDenunciation {
525 public_key: s_bh1.content_creator_pub_key,
526 slot: s_bh1.content.slot,
527 signature_1: s_bh1.signature,
528 signature_2: s_bh2.signature,
529 hash_1: *s_bh1_hash_content,
530 hash_2: *s_bh2_hash_content,
531 }))
532 }
533}
534
535#[allow(missing_docs)]
536#[derive(IntoPrimitive, Debug, TryFromPrimitive, VariantCount)]
537#[repr(u32)]
538pub enum DenunciationTypeId {
539 BlockHeader = 0,
540 Endorsement = 1,
541}
542
543impl From<&Denunciation> for DenunciationTypeId {
544 fn from(value: &Denunciation) -> Self {
545 match value {
546 Denunciation::Endorsement(_) => DenunciationTypeId::Endorsement,
547 Denunciation::BlockHeader(_) => DenunciationTypeId::BlockHeader,
548 }
549 }
550}
551
552#[allow(missing_docs)]
554#[derive(Error, Debug)]
555pub enum DenunciationError {
556 #[error("Invalid endorsements or block headers, cannot create denunciation: {0}")]
557 InvalidInput(String),
558 #[error("signature error: {0}")]
559 Signature(#[from] MassaSignatureError),
560 #[error("serialization error: {0}")]
561 Serialization(#[from] SerializeError),
562}
563
564struct EndorsementDenunciationSerializer {
568 slot_serializer: SlotSerializer,
569 u32_serializer: U32VarIntSerializer,
570 hash_serializer: HashSerializer,
571}
572
573impl EndorsementDenunciationSerializer {
574 const fn new() -> Self {
576 Self {
577 slot_serializer: SlotSerializer::new(),
578 u32_serializer: U32VarIntSerializer::new(),
579 hash_serializer: HashSerializer::new(),
580 }
581 }
582}
583
584impl Default for EndorsementDenunciationSerializer {
585 fn default() -> Self {
586 Self::new()
587 }
588}
589
590impl Serializer<EndorsementDenunciation> for EndorsementDenunciationSerializer {
591 fn serialize(
592 &self,
593 value: &EndorsementDenunciation,
594 buffer: &mut Vec<u8>,
595 ) -> Result<(), SerializeError> {
596 buffer.extend(value.public_key.to_bytes());
597 self.slot_serializer.serialize(&value.slot, buffer)?;
598 self.u32_serializer.serialize(&value.index, buffer)?;
599 self.hash_serializer.serialize(&value.hash_1, buffer)?;
600 self.hash_serializer.serialize(&value.hash_2, buffer)?;
601 buffer.extend(value.signature_1.to_bytes());
602 buffer.extend(value.signature_2.to_bytes());
603 Ok(())
604 }
605}
606
607struct EndorsementDenunciationDeserializer {
609 slot_deserializer: SlotDeserializer,
610 index_deserializer: U32VarIntDeserializer,
611 hash_deserializer: HashDeserializer,
612 pubkey_deserializer: PublicKeyDeserializer,
613 signature_deserializer: SignatureDeserializer,
614}
615
616impl EndorsementDenunciationDeserializer {
617 const fn new(thread_count: u8, endorsement_count: u32) -> Self {
619 EndorsementDenunciationDeserializer {
620 slot_deserializer: SlotDeserializer::new(
621 (Included(0), Included(u64::MAX)),
622 (Included(0), Excluded(thread_count)),
623 ),
624 index_deserializer: U32VarIntDeserializer::new(
625 Included(0),
626 Excluded(endorsement_count),
627 ),
628 hash_deserializer: HashDeserializer::new(),
629 pubkey_deserializer: PublicKeyDeserializer::new(),
630 signature_deserializer: SignatureDeserializer::new(),
631 }
632 }
633}
634
635impl Deserializer<EndorsementDenunciation> for EndorsementDenunciationDeserializer {
636 fn deserialize<'a, E: ParseError<&'a [u8]> + ContextError<&'a [u8]>>(
637 &self,
638 buffer: &'a [u8],
639 ) -> IResult<&'a [u8], EndorsementDenunciation, E> {
640 context(
641 "Failed Endorsement Denunciation deserialization",
642 tuple((
643 context("Failed public key deserialization", |input| {
644 self.pubkey_deserializer.deserialize(input)
645 }),
646 context("Failed slot deserialization", |input| {
647 self.slot_deserializer.deserialize(input)
648 }),
649 context("Failed slot deserialization", |input| {
650 self.index_deserializer.deserialize(input)
651 }),
652 context("Failed hash 1 deserialization", |input| {
653 self.hash_deserializer.deserialize(input)
654 }),
655 context("Failed hash 2 deserialization", |input| {
656 self.hash_deserializer.deserialize(input)
657 }),
658 context("Failed signature 1 deserialization", |input| {
659 self.signature_deserializer.deserialize(input)
660 }),
661 context("Failed signature 2 deserialization", |input| {
662 self.signature_deserializer.deserialize(input)
663 }),
664 )),
665 )
666 .map(
667 |(public_key, slot, index, hash_1, hash_2, signature_1, signature_2)| {
668 EndorsementDenunciation {
669 public_key,
670 slot,
671 index,
672 hash_1,
673 hash_2,
674 signature_1,
675 signature_2,
676 }
677 },
678 )
679 .parse(buffer)
680 }
681}
682
683struct BlockHeaderDenunciationSerializer {
685 slot_serializer: SlotSerializer,
686 hash_serializer: HashSerializer,
687}
688
689impl BlockHeaderDenunciationSerializer {
690 const fn new() -> Self {
692 Self {
693 slot_serializer: SlotSerializer::new(),
694 hash_serializer: HashSerializer::new(),
695 }
696 }
697}
698
699impl Default for BlockHeaderDenunciationSerializer {
700 fn default() -> Self {
701 Self::new()
702 }
703}
704
705impl Serializer<BlockHeaderDenunciation> for BlockHeaderDenunciationSerializer {
706 fn serialize(
707 &self,
708 value: &BlockHeaderDenunciation,
709 buffer: &mut Vec<u8>,
710 ) -> Result<(), SerializeError> {
711 buffer.extend(value.public_key.to_bytes());
712 self.slot_serializer.serialize(&value.slot, buffer)?;
713 self.hash_serializer.serialize(&value.hash_1, buffer)?;
714 self.hash_serializer.serialize(&value.hash_2, buffer)?;
715 buffer.extend(value.signature_1.to_bytes());
716 buffer.extend(value.signature_2.to_bytes());
717 Ok(())
718 }
719}
720
721struct BlockHeaderDenunciationDeserializer {
723 slot_deserializer: SlotDeserializer,
724 hash_deserializer: HashDeserializer,
725 pubkey_deserializer: PublicKeyDeserializer,
726 signature_deserializer: SignatureDeserializer,
727}
728
729impl BlockHeaderDenunciationDeserializer {
730 pub const fn new(thread_count: u8) -> Self {
732 Self {
733 slot_deserializer: SlotDeserializer::new(
734 (Included(0), Included(u64::MAX)),
735 (Included(0), Excluded(thread_count)),
736 ),
737 hash_deserializer: HashDeserializer::new(),
738 pubkey_deserializer: PublicKeyDeserializer::new(),
739 signature_deserializer: SignatureDeserializer::new(),
740 }
741 }
742}
743
744impl Deserializer<BlockHeaderDenunciation> for BlockHeaderDenunciationDeserializer {
745 fn deserialize<'a, E: ParseError<&'a [u8]> + ContextError<&'a [u8]>>(
746 &self,
747 buffer: &'a [u8],
748 ) -> IResult<&'a [u8], BlockHeaderDenunciation, E> {
749 context(
750 "Failed BlockHeader Denunciation deserialization",
751 tuple((
752 context("Failed public key deserialization", |input| {
753 self.pubkey_deserializer.deserialize(input)
754 }),
755 context("Failed slot deserialization", |input| {
756 self.slot_deserializer.deserialize(input)
757 }),
758 context("Failed hash 1 deserialization", |input| {
759 self.hash_deserializer.deserialize(input)
760 }),
761 context("Failed hash 2 deserialization", |input| {
762 self.hash_deserializer.deserialize(input)
763 }),
764 context("Failed signature 1 deserialization", |input| {
765 self.signature_deserializer.deserialize(input)
766 }),
767 context("Failed signature 2 deserialization", |input| {
768 self.signature_deserializer.deserialize(input)
769 }),
770 )),
771 )
772 .map(
773 |(public_key, slot, hash_1, hash_2, signature_1, signature_2)| {
774 BlockHeaderDenunciation {
775 public_key,
776 slot,
777 hash_1,
778 hash_2,
779 signature_1,
780 signature_2,
781 }
782 },
783 )
784 .parse(buffer)
785 }
786}
787
788pub struct DenunciationSerializer {
790 endo_de_serializer: EndorsementDenunciationSerializer,
791 blkh_de_serializer: BlockHeaderDenunciationSerializer,
792 type_id_serializer: U32VarIntSerializer,
793}
794
795impl DenunciationSerializer {
796 pub const fn new() -> Self {
798 Self {
799 endo_de_serializer: EndorsementDenunciationSerializer::new(),
800 blkh_de_serializer: BlockHeaderDenunciationSerializer::new(),
801 type_id_serializer: U32VarIntSerializer::new(),
802 }
803 }
804}
805
806impl Default for DenunciationSerializer {
807 fn default() -> Self {
808 Self::new()
809 }
810}
811
812impl Serializer<Denunciation> for DenunciationSerializer {
813 fn serialize(&self, value: &Denunciation, buffer: &mut Vec<u8>) -> Result<(), SerializeError> {
814 let de_type_id = DenunciationTypeId::from(value);
815 self.type_id_serializer
816 .serialize(&u32::from(de_type_id), buffer)?;
817 match value {
818 Denunciation::Endorsement(de) => {
819 self.endo_de_serializer.serialize(de, buffer)?;
820 }
821 Denunciation::BlockHeader(de) => {
822 self.blkh_de_serializer.serialize(de, buffer)?;
823 }
824 }
825 Ok(())
826 }
827}
828
829pub struct DenunciationDeserializer {
831 endo_de_deserializer: EndorsementDenunciationDeserializer,
832 blkh_de_deserializer: BlockHeaderDenunciationDeserializer,
833 type_id_deserializer: U32VarIntDeserializer,
834}
835
836impl DenunciationDeserializer {
837 pub const fn new(thread_count: u8, endorsement_count: u32) -> Self {
839 Self {
840 endo_de_deserializer: EndorsementDenunciationDeserializer::new(
841 thread_count,
842 endorsement_count,
843 ),
844 blkh_de_deserializer: BlockHeaderDenunciationDeserializer::new(thread_count),
845 type_id_deserializer: U32VarIntDeserializer::new(
846 Included(0),
847 Excluded(DenunciationTypeId::VARIANT_COUNT as u32),
848 ),
849 }
850 }
851}
852
853impl Deserializer<Denunciation> for DenunciationDeserializer {
854 fn deserialize<'a, E: ParseError<&'a [u8]> + ContextError<&'a [u8]>>(
855 &self,
856 buffer: &'a [u8],
857 ) -> IResult<&'a [u8], Denunciation, E> {
858 let (rem, de_type_id_) = context("Failed Denunciation type id deserialization", |input| {
859 self.type_id_deserializer.deserialize(input)
860 })
861 .parse(buffer)?;
862
863 let de_type_id = DenunciationTypeId::try_from(de_type_id_).map_err(|_| {
864 nom::Err::Error(ParseError::from_error_kind(
865 buffer,
866 nom::error::ErrorKind::Fail,
867 ))
868 })?;
869
870 match de_type_id {
871 DenunciationTypeId::Endorsement => {
872 let (rem2, endo_de) = self.endo_de_deserializer.deserialize(rem)?;
873 IResult::Ok((rem2, Denunciation::Endorsement(endo_de)))
874 }
875 DenunciationTypeId::BlockHeader => {
876 let (rem2, blkh_de) = self.blkh_de_deserializer.deserialize(rem)?;
877 IResult::Ok((rem2, Denunciation::BlockHeader(blkh_de)))
878 }
879 }
880 }
881}
882
883#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
888pub enum DenunciationIndex {
890 BlockHeader {
892 slot: Slot,
894 },
895 Endorsement {
897 slot: Slot,
899 index: u32,
901 },
902}
903
904impl DenunciationIndex {
905 pub fn get_slot(&self) -> &Slot {
907 match self {
908 DenunciationIndex::BlockHeader { slot } => slot,
909 DenunciationIndex::Endorsement { slot, .. } => slot,
910 }
911 }
912
913 pub fn get_index(&self) -> Option<&u32> {
915 match self {
916 DenunciationIndex::BlockHeader { .. } => None,
917 DenunciationIndex::Endorsement { slot: _, index } => Some(index),
918 }
919 }
920
921 pub fn get_hash(&self) -> Hash {
923 let mut buffer = u32::from(DenunciationIndexTypeId::from(self))
924 .to_le_bytes()
925 .to_vec();
926 match self {
927 DenunciationIndex::BlockHeader { slot } => buffer.extend(slot.to_bytes_key()),
928 DenunciationIndex::Endorsement { slot, index } => {
929 buffer.extend(slot.to_bytes_key());
930 buffer.extend(index.to_le_bytes());
931 }
932 }
933 Hash::compute_from(&buffer)
934 }
935}
936
937impl From<&Denunciation> for DenunciationIndex {
939 fn from(value: &Denunciation) -> Self {
940 match value {
941 Denunciation::Endorsement(endo_de) => DenunciationIndex::Endorsement {
942 slot: endo_de.slot,
943 index: endo_de.index,
944 },
945 Denunciation::BlockHeader(blkh_de) => {
946 DenunciationIndex::BlockHeader { slot: blkh_de.slot }
947 }
948 }
949 }
950}
951
952impl From<&DenunciationPrecursor> for DenunciationIndex {
954 fn from(value: &DenunciationPrecursor) -> Self {
955 match value {
956 DenunciationPrecursor::Endorsement(de_p) => DenunciationIndex::Endorsement {
957 slot: de_p.slot,
958 index: de_p.index,
959 },
960 DenunciationPrecursor::BlockHeader(de_p) => {
961 DenunciationIndex::BlockHeader { slot: de_p.slot }
962 }
963 }
964 }
965}
966
967impl Ord for DenunciationIndex {
968 fn cmp(&self, other: &Self) -> Ordering {
969 let self_key = (
972 self.get_slot(),
973 self.get_index().unwrap_or(&0),
974 u32::from(DenunciationIndexTypeId::from(self)),
975 );
976 let other_key = (
977 other.get_slot(),
978 other.get_index().unwrap_or(&0),
979 u32::from(DenunciationIndexTypeId::from(other)),
980 );
981 self_key.cmp(&other_key)
982 }
983}
984
985impl PartialOrd for DenunciationIndex {
986 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
987 Some(self.cmp(other))
988 }
989}
990
991#[derive(IntoPrimitive, Debug, Eq, PartialEq, TryFromPrimitive)]
996#[repr(u32)]
997enum DenunciationIndexTypeId {
998 BlockHeader = 0,
999 Endorsement = 1,
1000}
1001
1002impl From<&DenunciationIndex> for DenunciationIndexTypeId {
1003 fn from(value: &DenunciationIndex) -> Self {
1004 match value {
1005 DenunciationIndex::BlockHeader { .. } => DenunciationIndexTypeId::BlockHeader,
1006 DenunciationIndex::Endorsement { .. } => DenunciationIndexTypeId::Endorsement,
1007 }
1008 }
1009}
1010
1011#[derive(Clone)]
1012pub struct DenunciationIndexSerializer {
1014 u32_serializer: U32VarIntSerializer,
1015 slot_serializer: SlotSerializer,
1016 index_serializer: U32VarIntSerializer,
1017}
1018
1019impl DenunciationIndexSerializer {
1020 pub const fn new() -> Self {
1022 Self {
1023 u32_serializer: U32VarIntSerializer::new(),
1024 slot_serializer: SlotSerializer::new(),
1025 index_serializer: U32VarIntSerializer::new(),
1026 }
1027 }
1028}
1029
1030impl Default for DenunciationIndexSerializer {
1031 fn default() -> Self {
1032 Self::new()
1033 }
1034}
1035
1036impl Serializer<DenunciationIndex> for DenunciationIndexSerializer {
1037 fn serialize(
1038 &self,
1039 value: &DenunciationIndex,
1040 buffer: &mut Vec<u8>,
1041 ) -> Result<(), SerializeError> {
1042 match value {
1043 DenunciationIndex::BlockHeader { slot } => {
1044 self.u32_serializer
1045 .serialize(&u32::from(DenunciationIndexTypeId::BlockHeader), buffer)?;
1046 self.slot_serializer.serialize(slot, buffer)?;
1047 }
1048 DenunciationIndex::Endorsement { slot, index } => {
1049 self.u32_serializer
1050 .serialize(&u32::from(DenunciationIndexTypeId::Endorsement), buffer)?;
1051 self.slot_serializer.serialize(slot, buffer)?;
1052 self.index_serializer.serialize(index, buffer)?;
1053 }
1054 }
1055 Ok(())
1056 }
1057}
1058
1059#[derive(Clone)]
1060pub struct DenunciationIndexDeserializer {
1062 id_deserializer: U32VarIntDeserializer,
1063 slot_deserializer: SlotDeserializer,
1064 index_deserializer: U32VarIntDeserializer,
1065}
1066
1067impl DenunciationIndexDeserializer {
1068 pub const fn new(thread_count: u8, endorsement_count: u32) -> Self {
1070 Self {
1071 id_deserializer: U32VarIntDeserializer::new(Included(0), Included(u32::MAX)),
1072 slot_deserializer: SlotDeserializer::new(
1073 (Included(0), Included(u64::MAX)),
1074 (Included(0), Excluded(thread_count)),
1075 ),
1076 index_deserializer: U32VarIntDeserializer::new(
1077 Included(0),
1078 Included(endorsement_count),
1079 ),
1080 }
1081 }
1082}
1083
1084impl Deserializer<DenunciationIndex> for DenunciationIndexDeserializer {
1085 fn deserialize<'a, E: ParseError<&'a [u8]> + ContextError<&'a [u8]>>(
1086 &self,
1087 buffer: &'a [u8],
1088 ) -> IResult<&'a [u8], DenunciationIndex, E> {
1089 let (input, id) = self.id_deserializer.deserialize(buffer)?;
1090 let id = DenunciationIndexTypeId::try_from(id).map_err(|_| {
1091 nom::Err::Error(ParseError::from_error_kind(
1092 buffer,
1093 nom::error::ErrorKind::Eof,
1094 ))
1095 })?;
1096
1097 match id {
1098 DenunciationIndexTypeId::BlockHeader => {
1099 context("Failed slot deserialization", |input| {
1100 self.slot_deserializer.deserialize(input)
1101 })
1102 .map(|slot| DenunciationIndex::BlockHeader { slot })
1103 .parse(input)
1105 }
1106 DenunciationIndexTypeId::Endorsement => context(
1107 "Failed Endorsement denunciation index",
1108 tuple((
1109 context("Failed slot deserialization", |input| {
1110 self.slot_deserializer.deserialize(input)
1111 }),
1112 context("Failed index deserialization", |input| {
1113 self.index_deserializer.deserialize(input)
1114 }),
1115 )),
1116 )
1117 .map(|(slot, index)| DenunciationIndex::Endorsement { slot, index })
1118 .parse(input),
1119 }
1120 }
1121}
1122
1123#[derive(Debug, Clone, PartialEq)]
1129pub struct EndorsementDenunciationPrecursor {
1130 pub public_key: PublicKey,
1132 pub slot: Slot,
1134 pub index: u32,
1136 hash: Hash,
1138 signature: Signature,
1140}
1141
1142#[derive(Debug, Clone, PartialEq)]
1144pub struct BlockHeaderDenunciationPrecursor {
1145 pub public_key: PublicKey,
1147 pub slot: Slot,
1149 hash: Hash,
1151 signature: Signature,
1153}
1154
1155#[derive(Debug, Clone, PartialEq)]
1158pub enum DenunciationPrecursor {
1159 Endorsement(EndorsementDenunciationPrecursor),
1161 BlockHeader(BlockHeaderDenunciationPrecursor),
1163}
1164
1165impl DenunciationPrecursor {
1166 pub fn get_slot(&self) -> &Slot {
1168 match self {
1169 DenunciationPrecursor::Endorsement(endo_de_p) => &endo_de_p.slot,
1170 DenunciationPrecursor::BlockHeader(blkh_de_p) => &blkh_de_p.slot,
1171 }
1172 }
1173
1174 pub fn get_public_key(&self) -> &PublicKey {
1176 match self {
1177 DenunciationPrecursor::Endorsement(endo_de_p) => &endo_de_p.public_key,
1178 DenunciationPrecursor::BlockHeader(blkh_de_p) => &blkh_de_p.public_key,
1179 }
1180 }
1181}
1182
1183impl From<&SecureShareEndorsement> for DenunciationPrecursor {
1184 fn from(value: &SecureShareEndorsement) -> Self {
1185 DenunciationPrecursor::Endorsement(EndorsementDenunciationPrecursor {
1186 public_key: value.content_creator_pub_key,
1187 slot: value.content.slot,
1188 index: value.content.index,
1189 hash: *value.id.get_hash(),
1190 signature: value.signature,
1191 })
1192 }
1193}
1194
1195impl From<&SecuredHeader> for DenunciationPrecursor {
1196 fn from(value: &SecuredHeader) -> Self {
1197 DenunciationPrecursor::BlockHeader(BlockHeaderDenunciationPrecursor {
1198 public_key: value.content_creator_pub_key,
1199 slot: value.content.slot,
1200 hash: *value.id.get_hash(),
1201 signature: value.signature,
1202 })
1203 }
1204}
1205
1206impl TryFrom<(&DenunciationPrecursor, &DenunciationPrecursor)> for Denunciation {
1210 type Error = DenunciationError;
1211
1212 fn try_from(
1213 (de_p_1, de_p_2): (&DenunciationPrecursor, &DenunciationPrecursor),
1214 ) -> Result<Self, Self::Error> {
1215 Denunciation::try_from((de_p_1, de_p_2, None))
1216 }
1217}
1218
1219impl TryFrom<(&DenunciationPrecursor, &DenunciationPrecursor, Option<u64>)> for Denunciation {
1223 type Error = DenunciationError;
1224
1225 fn try_from(
1226 (de_p_1, de_p_2, sig_chain_id): (
1227 &DenunciationPrecursor,
1228 &DenunciationPrecursor,
1229 Option<u64>,
1230 ),
1231 ) -> Result<Self, Self::Error> {
1232 match (de_p_1, de_p_2) {
1233 (
1234 DenunciationPrecursor::BlockHeader(de_p_blkh_1),
1235 DenunciationPrecursor::BlockHeader(de_p_blkh_2),
1236 ) => {
1237 if de_p_blkh_1.slot != de_p_blkh_2.slot
1239 || de_p_blkh_1.public_key != de_p_blkh_2.public_key
1240 || de_p_blkh_1.hash == de_p_blkh_2.hash
1241 {
1242 return Err(DenunciationError::InvalidInput(
1243 format!("Not the same slot or public key or same hash for de precursor: {:?} & {:?}", de_p_blkh_1, de_p_blkh_2)
1244 ));
1245 }
1246
1247 let de_p_blkh_1_hash = BlockHeaderDenunciation::compute_hash_for_sig_verif(
1249 &de_p_blkh_1.public_key,
1250 &de_p_blkh_1.slot,
1251 &de_p_blkh_1.hash,
1252 sig_chain_id,
1253 );
1254 let de_p_blkh_2_hash = BlockHeaderDenunciation::compute_hash_for_sig_verif(
1255 &de_p_blkh_2.public_key,
1256 &de_p_blkh_2.slot,
1257 &de_p_blkh_2.hash,
1258 sig_chain_id,
1259 );
1260
1261 de_p_blkh_1
1262 .public_key
1263 .verify_signature(&de_p_blkh_1_hash, &de_p_blkh_1.signature)?;
1264 de_p_blkh_1
1265 .public_key
1266 .verify_signature(&de_p_blkh_2_hash, &de_p_blkh_2.signature)?;
1267
1268 Ok(Denunciation::BlockHeader(BlockHeaderDenunciation {
1269 public_key: de_p_blkh_1.public_key,
1270 slot: de_p_blkh_1.slot,
1271 signature_1: de_p_blkh_1.signature,
1272 signature_2: de_p_blkh_2.signature,
1273 hash_1: de_p_blkh_1.hash,
1274 hash_2: de_p_blkh_2.hash,
1275 }))
1276 }
1277 (
1278 DenunciationPrecursor::Endorsement(de_p_endo_1),
1279 DenunciationPrecursor::Endorsement(de_p_endo_2),
1280 ) => {
1281 if de_p_endo_1.slot != de_p_endo_2.slot
1283 || de_p_endo_1.index != de_p_endo_2.index
1284 || de_p_endo_1.public_key != de_p_endo_2.public_key
1285 || de_p_endo_1.hash == de_p_endo_2.hash
1286 {
1287 return Err(DenunciationError::InvalidInput(
1288 format!("Not the same slot, index or public key or same hash for de precursor: {:?} & {:?}", de_p_endo_1, de_p_endo_2)
1289 ));
1290 }
1291
1292 let de_p_endo_1_hash = EndorsementDenunciation::compute_hash_for_sig_verif(
1294 &de_p_endo_1.public_key,
1295 &de_p_endo_1.slot,
1296 &de_p_endo_1.index,
1297 &de_p_endo_1.hash,
1298 sig_chain_id,
1299 );
1300 let de_p_endo_2_hash = EndorsementDenunciation::compute_hash_for_sig_verif(
1301 &de_p_endo_2.public_key,
1302 &de_p_endo_2.slot,
1303 &de_p_endo_2.index,
1304 &de_p_endo_2.hash,
1305 sig_chain_id,
1306 );
1307
1308 de_p_endo_1
1309 .public_key
1310 .verify_signature(&de_p_endo_1_hash, &de_p_endo_1.signature)?;
1311 de_p_endo_1
1312 .public_key
1313 .verify_signature(&de_p_endo_2_hash, &de_p_endo_2.signature)?;
1314
1315 Ok(Denunciation::Endorsement(EndorsementDenunciation {
1316 public_key: de_p_endo_1.public_key,
1317 slot: de_p_endo_1.slot,
1318 index: de_p_endo_1.index,
1319 signature_1: de_p_endo_1.signature,
1320 signature_2: de_p_endo_2.signature,
1321 hash_1: de_p_endo_1.hash,
1322 hash_2: de_p_endo_2.hash,
1323 }))
1324 }
1325 _ => {
1326 Err(DenunciationError::InvalidInput(
1328 "Mixed endorsement and block header denunciation precursors".to_string(),
1329 ))
1330 }
1331 }
1332 }
1333}
1334
1335#[cfg(any(test, feature = "test-exports"))]
1340impl Denunciation {
1341 pub fn check_invariants(&self) -> Result<(), Box<dyn std::error::Error>> {
1343 if !self.is_valid_with_chain(Some(*crate::config::CHAINID)) {
1344 return Err(format!("Denunciation is invalid: {:?}", self).into());
1345 }
1346 Ok(())
1347 }
1348}
1349
1350#[cfg(test)]
1353mod tests {
1354 use super::*;
1355
1356 use massa_hash::Hash;
1357 use massa_serialization::DeserializeError;
1358 use massa_signature::KeyPair;
1359
1360 use crate::block_id::BlockId;
1361 use crate::config::{CHAINID, ENDORSEMENT_COUNT, THREAD_COUNT};
1362 use crate::endorsement::{Endorsement, EndorsementSerializer, SecureShareEndorsement};
1363 use crate::secure_share::{Id, SecureShareContent};
1364
1365 use crate::test_exports::{
1366 gen_block_headers_for_denunciation, gen_endorsements_for_denunciation,
1367 };
1368
1369 #[test]
1370 fn test_endorsement_denunciation() {
1371 let (_slot, _keypair, s_endorsement_1, s_endorsement_2, _s_endorsement_3) =
1373 gen_endorsements_for_denunciation(None, None);
1374 let denunciation: Denunciation = (&s_endorsement_1, &s_endorsement_2, Some(*CHAINID))
1375 .try_into()
1376 .unwrap();
1377
1378 assert!(denunciation.is_for_endorsement());
1379 assert!(denunciation.is_valid_with_chain(Some(*CHAINID)));
1380 }
1381
1382 #[test]
1383 fn test_endorsement_denunciation_invalid_1() {
1384 let (slot, keypair, s_endorsement_1, _s_endorsement_2, _s_endorsement_3) =
1385 gen_endorsements_for_denunciation(None, None);
1386
1387 let endorsement_4 = Endorsement {
1389 slot,
1390 index: 9,
1391 endorsed_block: BlockId::generate_from_hash(Hash::compute_from("foo".as_bytes())),
1392 };
1393 let s_endorsement_4 = Endorsement::new_verifiable(
1394 endorsement_4,
1395 EndorsementSerializer::new(),
1396 &keypair,
1397 *CHAINID,
1398 Some(*CHAINID),
1399 )
1400 .unwrap();
1401
1402 let denunciation =
1403 Denunciation::try_from((&s_endorsement_1, &s_endorsement_4, Some(*CHAINID)));
1404
1405 assert!(matches!(
1406 denunciation,
1407 Err(DenunciationError::InvalidInput(..))
1408 ));
1409
1410 let denunciation =
1412 Denunciation::try_from((&s_endorsement_1, &s_endorsement_1, Some(*CHAINID)));
1413
1414 assert!(matches!(
1415 denunciation,
1416 Err(DenunciationError::InvalidInput(..))
1417 ));
1418 }
1419
1420 #[test]
1421 fn test_endorsement_denunciation_is_for() {
1422 let (slot, keypair, s_endorsement_1, s_endorsement_2, s_endorsement_3) =
1423 gen_endorsements_for_denunciation(None, None);
1424
1425 let denunciation: Denunciation = (&s_endorsement_1, &s_endorsement_2, Some(*CHAINID))
1426 .try_into()
1427 .unwrap();
1428
1429 assert!(denunciation.is_for_endorsement());
1430 assert!(denunciation.is_valid_with_chain(Some(*CHAINID)));
1431
1432 let endorsement_4 = Endorsement {
1434 slot,
1435 index: 9,
1436 endorsed_block: BlockId::generate_from_hash(Hash::compute_from("foo".as_bytes())),
1437 };
1438 let s_endorsement_4 = Endorsement::new_verifiable(
1439 endorsement_4,
1440 EndorsementSerializer::new(),
1441 &keypair,
1442 *CHAINID,
1443 Some(*CHAINID),
1444 )
1445 .unwrap();
1446
1447 assert!(!denunciation
1448 .is_also_for_endorsement(&s_endorsement_4, Some(*CHAINID))
1449 .unwrap());
1450 assert!(denunciation
1451 .is_also_for_endorsement(&s_endorsement_3, Some(*CHAINID))
1452 .unwrap());
1453 assert!(denunciation.is_valid_with_chain(Some(*CHAINID)));
1454 }
1455
1456 #[test]
1457 fn test_block_header_denunciation() {
1458 let (_slot, _keypair, s_block_header_1, s_block_header_2, s_block_header_3) =
1460 gen_block_headers_for_denunciation(None, None);
1461 let denunciation: Denunciation = (&s_block_header_1, &s_block_header_2, Some(*CHAINID))
1462 .try_into()
1463 .unwrap();
1464
1465 assert!(denunciation.is_for_block_header());
1466 assert!(denunciation.is_valid_with_chain(Some(*CHAINID)));
1467 assert!(denunciation
1468 .is_also_for_block_header(&s_block_header_3, Some(*CHAINID))
1469 .unwrap());
1470 }
1471
1472 #[test]
1473 fn test_forge_invalid_denunciation() {
1474 let keypair = KeyPair::generate(0).unwrap();
1475 let slot_1 = Slot::new(4, 2);
1476 let slot_2 = Slot::new(3, 7);
1477
1478 let endorsement_1 = Endorsement {
1479 slot: slot_1,
1480 index: 0,
1481 endorsed_block: BlockId::generate_from_hash(Hash::compute_from("blk1".as_bytes())),
1482 };
1483
1484 let s_endorsement_1: SecureShareEndorsement = Endorsement::new_verifiable(
1485 endorsement_1,
1486 EndorsementSerializer::new(),
1487 &keypair,
1488 *CHAINID,
1489 Some(*CHAINID),
1490 )
1491 .unwrap();
1492
1493 let endorsement_2 = Endorsement {
1494 slot: slot_2,
1495 index: 0,
1496 endorsed_block: BlockId::generate_from_hash(Hash::compute_from("blk2".as_bytes())),
1497 };
1498
1499 let s_endorsement_2: SecureShareEndorsement = Endorsement::new_verifiable(
1500 endorsement_2,
1501 EndorsementSerializer::new(),
1502 &keypair,
1503 *CHAINID,
1504 Some(*CHAINID),
1505 )
1506 .unwrap();
1507
1508 let de_forged_1 = Denunciation::Endorsement(EndorsementDenunciation {
1510 public_key: keypair.get_public_key(),
1511 slot: slot_1,
1512 index: 0,
1513 hash_1: *s_endorsement_1.id.get_hash(), hash_2: *s_endorsement_1.id.get_hash(),
1515 signature_1: s_endorsement_1.signature,
1516 signature_2: s_endorsement_1.signature,
1517 });
1518
1519 assert!(!de_forged_1.is_valid_with_chain(Some(*CHAINID)));
1521
1522 let de_forged_2 = Denunciation::Endorsement(EndorsementDenunciation {
1524 public_key: keypair.get_public_key(),
1525 slot: slot_2,
1526 index: 0,
1527 hash_1: *s_endorsement_1.id.get_hash(),
1528 hash_2: *s_endorsement_2.id.get_hash(),
1529 signature_1: s_endorsement_1.signature,
1530 signature_2: s_endorsement_2.signature,
1531 });
1532
1533 assert!(!de_forged_2.is_valid_with_chain(Some(*CHAINID)));
1536 }
1537
1538 #[test]
1540 fn test_endorsement_denunciation_ser_der() {
1541 let (_, _, s_endorsement_1, s_endorsement_2, _) =
1542 gen_endorsements_for_denunciation(None, None);
1543
1544 let denunciation =
1545 Denunciation::try_from((&s_endorsement_1, &s_endorsement_2, Some(*CHAINID))).unwrap();
1546
1547 let mut buffer = Vec::new();
1548 let de_ser = EndorsementDenunciationSerializer::new();
1549
1550 match denunciation {
1551 Denunciation::Endorsement(de) => {
1552 de_ser.serialize(&de, &mut buffer).unwrap();
1553 let de_der =
1554 EndorsementDenunciationDeserializer::new(THREAD_COUNT, ENDORSEMENT_COUNT);
1555
1556 let (rem, de_der_res) = de_der.deserialize::<DeserializeError>(&buffer).unwrap();
1557
1558 assert!(rem.is_empty());
1559 assert_eq!(de, de_der_res);
1560 }
1561 Denunciation::BlockHeader(_) => {
1562 unimplemented!()
1563 }
1564 }
1565 }
1566
1567 #[test]
1568 fn test_block_header_denunciation_ser_der() {
1569 let (_, _, s_block_header_1, s_block_header_2, _) =
1570 gen_block_headers_for_denunciation(None, None);
1571 let denunciation: Denunciation = (&s_block_header_1, &s_block_header_2, Some(*CHAINID))
1572 .try_into()
1573 .unwrap();
1574
1575 let mut buffer = Vec::new();
1576 let de_ser = BlockHeaderDenunciationSerializer::new();
1577
1578 match denunciation {
1579 Denunciation::Endorsement(_) => {
1580 unimplemented!()
1581 }
1582 Denunciation::BlockHeader(de) => {
1583 de_ser.serialize(&de, &mut buffer).unwrap();
1584 let de_der = BlockHeaderDenunciationDeserializer::new(THREAD_COUNT);
1585
1586 let (rem, de_der_res) = de_der.deserialize::<DeserializeError>(&buffer).unwrap();
1587
1588 assert!(rem.is_empty());
1589 assert_eq!(de, de_der_res);
1590 }
1591 }
1592 }
1593
1594 #[test]
1595 fn test_denunciation_ser_der() {
1596 let (_, _, s_block_header_1, s_block_header_2, _) =
1597 gen_block_headers_for_denunciation(None, None);
1598 let denunciation: Denunciation = (&s_block_header_1, &s_block_header_2, Some(*CHAINID))
1599 .try_into()
1600 .unwrap();
1601
1602 let mut buffer = Vec::new();
1603 let de_ser = DenunciationSerializer::new();
1604
1605 de_ser.serialize(&denunciation, &mut buffer).unwrap();
1606 let de_der = DenunciationDeserializer::new(THREAD_COUNT, ENDORSEMENT_COUNT);
1607
1608 let (rem, de_der_res) = de_der.deserialize::<DeserializeError>(&buffer).unwrap();
1609
1610 assert!(rem.is_empty());
1611 assert_eq!(denunciation, de_der_res);
1612
1613 let (_, _, s_endorsement_1, s_endorsement_2, _) =
1614 gen_endorsements_for_denunciation(None, None);
1615 let denunciation =
1616 Denunciation::try_from((&s_endorsement_1, &s_endorsement_2, Some(*CHAINID))).unwrap();
1617 buffer.clear();
1618
1619 de_ser.serialize(&denunciation, &mut buffer).unwrap();
1620 let (rem, de_der_res) = de_der.deserialize::<DeserializeError>(&buffer).unwrap();
1621 assert!(rem.is_empty());
1622 assert_eq!(denunciation, de_der_res);
1623 }
1624
1625 #[test]
1626 fn test_denunciation_precursor() {
1627 let (_, _, s_block_header_1, s_block_header_2, _) =
1628 gen_block_headers_for_denunciation(None, None);
1629 let denunciation: Denunciation = (&s_block_header_1, &s_block_header_2, Some(*CHAINID))
1630 .try_into()
1631 .unwrap();
1632
1633 let de_p_1 = DenunciationPrecursor::from(&s_block_header_1);
1634 let de_p_2 = DenunciationPrecursor::from(&s_block_header_2);
1635 let denunciation_2: Denunciation = (&de_p_1, &de_p_2, Some(*CHAINID)).try_into().unwrap();
1636
1637 assert_eq!(denunciation, denunciation_2);
1638
1639 let (_, _, s_endorsement_1, s_endorsement_2, _) =
1640 gen_endorsements_for_denunciation(None, None);
1641 let denunciation_3 =
1642 Denunciation::try_from((&s_endorsement_1, &s_endorsement_2, Some(*CHAINID))).unwrap();
1643
1644 let de_p_3 = DenunciationPrecursor::from(&s_endorsement_1);
1645 let de_p_4 = DenunciationPrecursor::from(&s_endorsement_2);
1646 let denunciation_4: Denunciation = (&de_p_3, &de_p_4, Some(*CHAINID)).try_into().unwrap();
1647
1648 assert_eq!(denunciation_3, denunciation_4);
1649 }
1650
1651 #[test]
1652 fn test_denunciation_index_ser_der() {
1653 let (_, _, s_block_header_1, s_block_header_2, _) =
1654 gen_block_headers_for_denunciation(None, None);
1655 let denunciation_1: Denunciation = (&s_block_header_1, &s_block_header_2, Some(*CHAINID))
1656 .try_into()
1657 .unwrap();
1658 let denunciation_index_1 = DenunciationIndex::from(&denunciation_1);
1659
1660 let (_, _, s_endorsement_1, s_endorsement_2, _) =
1661 gen_endorsements_for_denunciation(None, None);
1662 let denunciation_2 =
1663 Denunciation::try_from((&s_endorsement_1, &s_endorsement_2, Some(*CHAINID))).unwrap();
1664 let denunciation_index_2 = DenunciationIndex::from(&denunciation_2);
1665
1666 let mut buffer = Vec::new();
1667 let de_idx_ser = DenunciationIndexSerializer::new();
1668 de_idx_ser
1669 .serialize(&denunciation_index_1, &mut buffer)
1670 .unwrap();
1671 let de_idx_der = DenunciationIndexDeserializer::new(THREAD_COUNT, ENDORSEMENT_COUNT);
1672 let (rem, de_idx_der_res) = de_idx_der.deserialize::<DeserializeError>(&buffer).unwrap();
1673
1674 assert!(rem.is_empty());
1675 assert_eq!(denunciation_index_1, de_idx_der_res);
1676
1677 let mut buffer = Vec::new();
1678 let de_idx_ser = DenunciationIndexSerializer::new();
1679 de_idx_ser
1680 .serialize(&denunciation_index_2, &mut buffer)
1681 .unwrap();
1682 let de_idx_der = DenunciationIndexDeserializer::new(THREAD_COUNT, ENDORSEMENT_COUNT);
1683 let (rem, de_idx_der_res) = de_idx_der.deserialize::<DeserializeError>(&buffer).unwrap();
1684
1685 assert!(rem.is_empty());
1686 assert_eq!(denunciation_index_2, de_idx_der_res);
1687 }
1688
1689 fn make_endorsement(
1699 keypair: &KeyPair,
1700 slot: Slot,
1701 endorsed_block: &str,
1702 chain_id: u64,
1703 sig_chain_id: Option<u64>,
1704 ) -> SecureShareEndorsement {
1705 Endorsement::new_verifiable(
1706 Endorsement {
1707 slot,
1708 index: 0,
1709 endorsed_block: BlockId::generate_from_hash(Hash::compute_from(
1710 endorsed_block.as_bytes(),
1711 )),
1712 },
1713 EndorsementSerializer::new(),
1714 keypair,
1715 chain_id,
1716 sig_chain_id,
1717 )
1718 .unwrap()
1719 }
1720
1721 #[test]
1722 fn test_endorsement_denunciation_cross_chain_rejected() {
1723 let keypair = KeyPair::generate(0).unwrap();
1724 let slot = Slot::new(3, 7);
1725 let chain_a: u64 = 77658377; let chain_b: u64 = 77658376; let e_a1 = make_endorsement(&keypair, slot, "blk_a1", chain_a, Some(chain_a));
1730 let e_a2 = make_endorsement(&keypair, slot, "blk_a2", chain_a, Some(chain_a));
1731
1732 let de_same =
1734 Denunciation::try_from((&e_a1, &e_a2, Some(chain_a))).expect("same-chain denunciation");
1735 assert!(de_same.is_valid_with_chain(Some(chain_a)));
1736
1737 let e_b2 = make_endorsement(&keypair, slot, "blk_b2", chain_b, Some(chain_b));
1739
1740 let de_cross = Denunciation::try_from((&e_a1, &e_b2, Some(chain_a)));
1743 assert!(de_cross.is_err(), "cross-chain denunciation must not build");
1744 assert!(!Denunciation::try_from((&e_a1, &e_b2, None))
1746 .map(|d| d.is_valid_with_chain(Some(chain_a)))
1747 .unwrap_or(false));
1748 }
1749
1750 #[test]
1759 fn test_endorsement_denunciation_migration_no_partition() {
1760 let keypair = KeyPair::generate(0).unwrap();
1761 let slot = Slot::new(3, 7);
1762 let chain: u64 = *CHAINID;
1763
1764 let e_l1 = make_endorsement(&keypair, slot, "blk_l1", chain, None);
1766 let e_l2 = make_endorsement(&keypair, slot, "blk_l2", chain, None);
1767 let de_legacy = Denunciation::try_from((&e_l1, &e_l2, None)).expect("legacy denunciation");
1768 assert!(de_legacy.is_valid_with_chain(None));
1769 assert!(!de_legacy.is_valid_with_chain(Some(chain)));
1773
1774 let e_n1 = make_endorsement(&keypair, slot, "blk_n1", chain, Some(chain));
1776 let e_n2 = make_endorsement(&keypair, slot, "blk_n2", chain, Some(chain));
1777 let de_scoped =
1778 Denunciation::try_from((&e_n1, &e_n2, Some(chain))).expect("scoped denunciation");
1779 assert!(de_scoped.is_valid_with_chain(Some(chain)));
1780 assert!(!de_scoped.is_valid_with_chain(None));
1781 }
1782
1783 fn make_header(
1784 keypair: &KeyPair,
1785 slot: Slot,
1786 merkle: &str,
1787 chain_id: u64,
1788 sig_chain_id: Option<u64>,
1789 ) -> crate::block_header::SecuredHeader {
1790 crate::block_header::BlockHeader::new_verifiable(
1791 crate::block_header::BlockHeader {
1792 current_version: 0,
1793 announced_version: None,
1794 slot,
1795 parents: Vec::new(),
1796 operation_merkle_root: Hash::compute_from(merkle.as_bytes()),
1797 endorsements: Vec::new(),
1798 denunciations: Vec::new(),
1799 },
1800 crate::block_header::BlockHeaderSerializer::new(),
1801 keypair,
1802 chain_id,
1803 sig_chain_id,
1804 )
1805 .unwrap()
1806 }
1807
1808 #[test]
1809 fn test_block_header_denunciation_cross_chain_rejected() {
1810 let keypair = KeyPair::generate(0).unwrap();
1811 let slot = Slot::new(3, 7);
1812 let chain_a: u64 = 77658377;
1813 let chain_b: u64 = 77658376;
1814
1815 let h_a1 = make_header(&keypair, slot, "hdr_a1", chain_a, Some(chain_a));
1816 let h_a2 = make_header(&keypair, slot, "hdr_a2", chain_a, Some(chain_a));
1817 let de_same =
1818 Denunciation::try_from((&h_a1, &h_a2, Some(chain_a))).expect("same-chain header de");
1819 assert!(de_same.is_valid_with_chain(Some(chain_a)));
1820
1821 let h_b2 = make_header(&keypair, slot, "hdr_b2", chain_b, Some(chain_b));
1822 assert!(
1823 Denunciation::try_from((&h_a1, &h_b2, Some(chain_a))).is_err(),
1824 "cross-chain header denunciation must not build"
1825 );
1826 }
1827}