1use crate::block_id::{BlockId, BlockIdDeserializer, BlockIdSerializer};
2use crate::denunciation::{Denunciation, DenunciationDeserializer, DenunciationSerializer};
3use crate::endorsement::{
4 Endorsement, EndorsementDeserializerLW, EndorsementId, EndorsementSerializer,
5 EndorsementSerializerLW, SecureShareEndorsement,
6};
7use crate::secure_share::{
8 SecureShare, SecureShareContent, SecureShareDeserializer, SecureShareSerializer,
9};
10use crate::slot::{Slot, SlotDeserializer, SlotSerializer};
11use massa_hash::{Hash, HashDeserializer};
12use massa_serialization::{
13 Deserializer, OptionDeserializer, OptionSerializer, SerializeError, Serializer,
14 U32VarIntDeserializer, U32VarIntSerializer,
15};
16use massa_signature::PublicKey;
17use nom::branch::alt;
18use nom::bytes::complete::tag;
19use nom::error::{context, ContextError, ParseError};
20use nom::multi::{count, length_count};
21use nom::sequence::{preceded, tuple};
22use nom::{IResult, Parser};
23use serde::{Deserialize, Serialize};
24use std::collections::Bound::{Excluded, Included};
25use std::collections::HashSet;
26use std::fmt::Formatter;
27
28#[derive(Debug, Clone, Serialize, Deserialize)]
30pub struct BlockHeader {
31 pub current_version: u32,
33 pub announced_version: Option<u32>,
35 pub slot: Slot,
37 pub parents: Vec<BlockId>,
39 pub operation_merkle_root: Hash,
41 pub endorsements: Vec<SecureShareEndorsement>,
43 pub denunciations: Vec<Denunciation>,
45}
46
47#[cfg(any(test, feature = "test-exports"))]
49impl BlockHeader {
50 fn assert_invariants(
54 &self,
55 thread_count: u8,
56 endorsement_count: u32,
57 ) -> Result<(), Box<dyn std::error::Error>> {
58 if self.slot.period == 0 {
59 if !self.parents.is_empty() {
60 return Err("Invariant broken: genesis block with parent(s)".into());
61 }
62 if !self.endorsements.is_empty() {
63 return Err("Invariant broken: genesis block with endorsement(s)".into());
64 }
65 } else {
66 if self.parents.len() != thread_count as usize {
67 return Err(
68 "Invariant broken: non-genesis block with incorrect number of parents".into(),
69 );
70 }
71 if self.endorsements.len() > endorsement_count as usize {
72 return Err("Invariant broken: endorsement count too high".into());
73 }
74
75 let parent_id = self.parents[self.slot.thread as usize];
76 for endo in self.endorsements.iter() {
77 if endo.content.endorsed_block != parent_id {
78 return Err("Invariant broken: endorsement doesn't match parent".into());
79 }
80 }
81 }
82
83 let mut set = HashSet::new();
85 for endo in self.endorsements.iter() {
86 if !set.insert(endo.content.index) {
87 return Err("Endorsement duplicate index found".into());
88 }
89 }
90
91 for de in self.denunciations.iter() {
92 de.check_invariants()?;
93 }
94
95 Ok(())
96 }
97}
98
99pub type SecuredHeader = SecureShare<BlockHeader, BlockId>;
101
102impl SecureShareContent for BlockHeader {
103 fn compute_signed_hash(
111 &self,
112 public_key: &PublicKey,
113 content_hash: &Hash,
114 sig_chain_id: Option<u64>,
115 ) -> Hash {
116 let mut signed_data: Vec<u8> = Vec::new();
117 signed_data.extend(public_key.to_bytes());
118 signed_data.extend(BlockHeaderDenunciationData::new(self.slot, sig_chain_id).to_bytes());
119 signed_data.extend(content_hash.to_bytes());
120 Hash::compute_from(&signed_data)
121 }
122}
123
124impl SecuredHeader {
125 pub fn get_fitness(&self) -> u64 {
127 (self.content.endorsements.len() as u64) + 1
128 }
129 #[allow(dead_code)]
131 #[cfg(any(test, feature = "test-exports"))]
132 pub(crate) fn assert_invariants(
133 &self,
134 thread_count: u8,
135 endorsement_count: u32,
136 ) -> Result<(), Box<dyn std::error::Error>> {
137 self.content
138 .assert_invariants(thread_count, endorsement_count)?;
139 self.verify_signature(Some(*crate::config::CHAINID))
140 .map_err(|er| format!("{}", er).into())
141 }
142}
143
144pub struct BlockHeaderSerializer {
146 slot_serializer: SlotSerializer,
147 endorsement_serializer: SecureShareSerializer,
148 endorsement_content_serializer: EndorsementSerializerLW,
149 denunciation_serializer: DenunciationSerializer,
150 u32_serializer: U32VarIntSerializer,
151 opt_serializer: OptionSerializer<u32, U32VarIntSerializer>,
152 block_id_serializer: BlockIdSerializer,
153}
154
155impl BlockHeaderSerializer {
156 pub fn new() -> Self {
158 Self {
159 slot_serializer: SlotSerializer::new(),
160 endorsement_serializer: SecureShareSerializer::new(),
161 u32_serializer: U32VarIntSerializer::new(),
162 opt_serializer: OptionSerializer::new(U32VarIntSerializer),
163 endorsement_content_serializer: EndorsementSerializerLW::new(),
164 denunciation_serializer: DenunciationSerializer::new(),
165 block_id_serializer: BlockIdSerializer::new(),
166 }
167 }
168}
169
170impl Default for BlockHeaderSerializer {
171 fn default() -> Self {
172 Self::new()
173 }
174}
175
176impl Serializer<BlockHeader> for BlockHeaderSerializer {
177 fn serialize(&self, value: &BlockHeader, buffer: &mut Vec<u8>) -> Result<(), SerializeError> {
230 self.u32_serializer
232 .serialize(&value.current_version, buffer)?;
233 self.opt_serializer
234 .serialize(&value.announced_version, buffer)?;
235
236 self.slot_serializer.serialize(&value.slot, buffer)?;
238
239 if value.parents.is_empty() {
241 buffer.push(0);
242 } else {
243 buffer.push(1);
244 }
245 for parent_h in value.parents.iter() {
246 self.block_id_serializer.serialize(parent_h, buffer)?;
247 }
248
249 buffer.extend(value.operation_merkle_root.to_bytes());
251
252 self.u32_serializer.serialize(
253 &value.endorsements.len().try_into().map_err(|err| {
254 SerializeError::GeneralError(format!("too many endorsements: {}", err))
255 })?,
256 buffer,
257 )?;
258
259 for endorsement in value.endorsements.iter() {
260 self.endorsement_serializer.serialize_with(
261 &self.endorsement_content_serializer,
262 endorsement,
263 buffer,
264 )?;
265 }
266 self.u32_serializer.serialize(
267 &value.denunciations.len().try_into().map_err(|err| {
268 SerializeError::GeneralError(format!("too many denunciations: {}", err))
269 })?,
270 buffer,
271 )?;
272 for denunciation in value.denunciations.iter() {
273 self.denunciation_serializer
274 .serialize(denunciation, buffer)?;
275 }
276
277 Ok(())
278 }
279}
280
281pub struct BlockHeaderDeserializer {
283 slot_deserializer: SlotDeserializer,
284 endorsement_serializer: EndorsementSerializer,
285 endorsement_len_deserializer: U32VarIntDeserializer,
286 hash_deserializer: HashDeserializer,
287 thread_count: u8,
288 endorsement_count: u32,
289 last_start_period: Option<u64>,
290 denunciation_len_deserializer: U32VarIntDeserializer,
291 denunciation_deserializer: DenunciationDeserializer,
292 network_versions_deserializer: U32VarIntDeserializer,
293 opt_deserializer: OptionDeserializer<u32, U32VarIntDeserializer>,
294 block_id_deserializer: BlockIdDeserializer,
295 chain_id: u64,
296}
297
298impl BlockHeaderDeserializer {
299 pub fn new(
302 thread_count: u8,
303 endorsement_count: u32,
304 max_denunciations_in_block_header: u32,
305 last_start_period: Option<u64>,
306 chain_id: u64,
307 ) -> Self {
308 Self {
309 slot_deserializer: SlotDeserializer::new(
310 (Included(0), Included(u64::MAX)),
311 (Included(0), Excluded(thread_count)),
312 ),
313 endorsement_serializer: EndorsementSerializer::new(),
314 endorsement_len_deserializer: U32VarIntDeserializer::new(
315 Included(0),
316 Included(endorsement_count),
317 ),
318 hash_deserializer: HashDeserializer::new(),
319 denunciation_len_deserializer: U32VarIntDeserializer::new(
320 Included(0),
321 Included(max_denunciations_in_block_header),
322 ),
323 network_versions_deserializer: U32VarIntDeserializer::new(
324 Included(0),
325 Included(u32::MAX),
326 ),
327 opt_deserializer: OptionDeserializer::new(U32VarIntDeserializer::new(
328 Included(0),
329 Included(u32::MAX),
330 )),
331 denunciation_deserializer: DenunciationDeserializer::new(
332 thread_count,
333 endorsement_count,
334 ),
335 block_id_deserializer: BlockIdDeserializer::new(),
336 thread_count,
337 endorsement_count,
338 last_start_period,
339 chain_id,
340 }
341 }
342}
343
344impl Deserializer<BlockHeader> for BlockHeaderDeserializer {
345 #[allow(clippy::type_complexity)]
403 fn deserialize<'a, E: ParseError<&'a [u8]> + ContextError<&'a [u8]>>(
404 &self,
405 buffer: &'a [u8],
406 ) -> IResult<&'a [u8], BlockHeader, E> {
407 let (rest, (current_version, announced_version, slot, parents, operation_merkle_root)): (
408 &[u8],
409 (u32, Option<u32>, Slot, Vec<BlockId>, Hash),
410 ) = context("Failed BlockHeader deserialization", |input| {
411 let (rest, (current_version, announced_version, slot, parents)) = tuple((
412 context("Failed current_version deserialization", |input| {
413 self.network_versions_deserializer.deserialize(input)
414 }),
415 context("Failed announced_version deserialization", |input| {
416 self.opt_deserializer.deserialize(input)
417 }),
418 context("Failed slot deserialization", |input| {
419 self.slot_deserializer.deserialize(input)
420 }),
421 context(
422 "Failed parents deserialization",
423 alt((
424 preceded(tag(&[0]), |input| Ok((input, Vec::new()))),
425 preceded(
426 tag(&[1]),
427 count(
428 context("Failed block_id deserialization", |input| {
429 self.block_id_deserializer.deserialize(input)
430 }),
431 self.thread_count as usize,
432 ),
433 ),
434 )),
435 ),
436 ))
437 .parse(input)?;
438
439 if let Some(last_start_period) = self.last_start_period {
441 if slot.period == last_start_period && !parents.is_empty() {
442 return Err(nom::Err::Failure(ContextError::add_context(
443 rest,
444 "Genesis block cannot contain parents",
445 ParseError::from_error_kind(rest, nom::error::ErrorKind::Fail),
446 )));
447 } else if slot.period != last_start_period
448 && parents.len() != self.thread_count as usize
449 {
450 return Err(nom::Err::Failure(ContextError::add_context(
451 rest,
452 "Non-genesis block must have same numbers of parents as threads count",
453 ParseError::from_error_kind(rest, nom::error::ErrorKind::Fail),
454 )));
455 }
456 }
457
458 let (rest, merkle) = context("Failed operation_merkle_root", |input| {
459 self.hash_deserializer.deserialize(input)
460 })
461 .parse(rest)?;
462 Ok((
463 rest,
464 (current_version, announced_version, slot, parents, merkle),
465 ))
466 })
467 .parse(buffer)?;
468
469 if parents.is_empty() {
470 let res = BlockHeader {
471 current_version,
472 announced_version,
473 slot,
474 parents,
475 operation_merkle_root,
476 endorsements: Vec::new(),
477 denunciations: Vec::new(),
478 };
479
480 #[cfg(any(test, feature = "test-exports"))]
482 res.assert_invariants(self.thread_count, self.endorsement_count)
483 .unwrap();
484
485 let (rest2, _) = tag(&[0, 0])(rest)?;
488 return Ok((rest2, res));
489 }
490
491 let endorsement_deserializer = SecureShareDeserializer::new(
493 EndorsementDeserializerLW::new(
494 self.endorsement_count,
495 slot,
496 parents[slot.thread as usize],
497 ),
498 self.chain_id,
499 );
500
501 let parent_id = parents[slot.thread as usize];
502 let (rest, endorsements): (&[u8], Vec<SecureShare<Endorsement, EndorsementId>>) = context(
503 "Failed endorsements deserialization",
504 length_count::<&[u8], SecureShare<Endorsement, EndorsementId>, u32, E, _, _>(
505 context("Failed length deserialization", |input| {
506 self.endorsement_len_deserializer.deserialize(input)
507 }),
508 context("Failed endorsement deserialization", |input| {
509 let (rest, endo) = endorsement_deserializer
510 .deserialize_with(&self.endorsement_serializer, input)?;
511
512 if endo.content.endorsed_block != parent_id {
513 return Err(nom::Err::Failure(ContextError::add_context(
514 rest,
515 "Endorsement does not match block parents",
516 ParseError::from_error_kind(rest, nom::error::ErrorKind::Fail),
517 )));
518 }
519
520 Ok((rest, endo))
521 }),
522 ),
523 )
524 .parse(rest)?;
525
526 let mut set = HashSet::new();
527 for end in endorsements.iter() {
528 if !set.insert(end.content.index) {
529 return Err(nom::Err::Failure(ContextError::add_context(
530 rest,
531 "Duplicate endorsement index found",
532 ParseError::from_error_kind(rest, nom::error::ErrorKind::Fail),
533 )));
534 }
535 }
536
537 let (rest, denunciations): (&[u8], Vec<Denunciation>) = context(
538 "Failed denunciations deserialization",
539 length_count::<&[u8], Denunciation, u32, E, _, _>(
540 context("Failed length deserialization", |input| {
541 let (res, count) = self.denunciation_len_deserializer.deserialize(input)?;
542 IResult::Ok((res, count))
543 }),
544 context("Failed denunciation deserialization", |input| {
545 self.denunciation_deserializer.deserialize(input)
546 }),
547 ),
548 )
549 .parse(rest)?;
550
551 let header = BlockHeader {
552 current_version,
553 announced_version,
554 slot,
555 parents,
556 operation_merkle_root,
557 endorsements,
558 denunciations,
559 };
560
561 #[cfg(any(test, feature = "test-exports"))]
563 header
564 .assert_invariants(self.thread_count, self.endorsement_count)
565 .unwrap();
566
567 Ok((rest, header))
568 }
569}
570
571impl std::fmt::Display for BlockHeader {
572 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
573 writeln!(
574 f,
575 "\t(period: {}, thread: {})",
576 self.slot.period, self.slot.thread,
577 )?;
578 writeln!(f, "\tMerkle root: {}", self.operation_merkle_root,)?;
579 writeln!(f, "\tParents: ")?;
580 for id in self.parents.iter() {
581 let str_id = id.to_string();
582 writeln!(f, "\t\t{}", str_id)?;
583 }
584 if self.parents.is_empty() {
585 writeln!(f, "No parents found: This is a genesis header")?;
586 }
587 writeln!(f, "\tEndorsements:")?;
588 for ed in self.endorsements.iter() {
589 writeln!(f, "\t\t-----")?;
590 writeln!(f, "\t\tId: {}", ed.id)?;
591 writeln!(f, "\t\tIndex: {}", ed.content.index)?;
592 writeln!(f, "\t\tEndorsed slot: {}", ed.content.slot)?;
593 writeln!(
594 f,
595 "\t\tEndorser's public key: {}",
596 ed.content_creator_pub_key
597 )?;
598 writeln!(f, "\t\tEndorsed block: {}", ed.content.endorsed_block)?;
599 writeln!(f, "\t\tSignature: {}", ed.signature)?;
600 }
601 if self.endorsements.is_empty() {
602 writeln!(f, "\tNo endorsements found")?;
603 }
604 Ok(())
605 }
606}
607
608#[derive(Debug)]
610pub struct BlockHeaderDenunciationData {
611 slot: Slot,
612 chain_id: Option<u64>,
615}
616
617impl BlockHeaderDenunciationData {
618 pub fn new(slot: Slot, chain_id: Option<u64>) -> Self {
621 Self { slot, chain_id }
622 }
623
624 #[allow(dead_code)]
628 pub fn new_legacy(slot: Slot) -> Self {
629 Self {
630 slot,
631 chain_id: None,
632 }
633 }
634
635 pub fn to_bytes(&self) -> Vec<u8> {
637 let mut buf = Vec::new();
638 buf.extend(self.slot.to_bytes_key());
639 if let Some(chain_id) = self.chain_id {
640 buf.extend(chain_id.to_be_bytes());
641 }
642 buf
643 }
644}
645
646#[cfg(test)]
647mod test {
648 use super::*;
649 use massa_serialization::DeserializeError;
650 use serde_json::Value;
651
652 use crate::config::{
653 CHAINID, ENDORSEMENT_COUNT, MAX_DENUNCIATIONS_PER_BLOCK_HEADER, THREAD_COUNT,
654 };
655
656 use crate::test_exports::{
657 gen_block_headers_for_denunciation, gen_endorsements_for_denunciation,
658 };
659 use massa_signature::{verify_signature_batch, KeyPair};
660
661 impl PartialEq for BlockHeader {
663 fn eq(&self, other: &Self) -> bool {
664 self.slot == other.slot
665 && self.parents == other.parents
666 && self.operation_merkle_root == other.operation_merkle_root
667 && self.endorsements == other.endorsements
668 && self.denunciations == other.denunciations
669 }
670 }
671
672 #[test]
673 fn test_block_header_ser_der() {
674 let keypair = KeyPair::generate(0).unwrap();
675
676 let slot = Slot::new(7, 1);
677 let parents_1: Vec<BlockId> = (0..THREAD_COUNT)
678 .map(|i| BlockId::generate_from_hash(Hash::compute_from(&[i])))
679 .collect();
680
681 let endorsement_1 = Endorsement {
682 slot,
683 index: 1,
684 endorsed_block: parents_1[1],
685 };
686
687 assert_eq!(parents_1[1], endorsement_1.endorsed_block);
688
689 let s_endorsement_1: SecureShareEndorsement = Endorsement::new_verifiable(
690 endorsement_1,
691 EndorsementSerializer::new(),
692 &keypair,
693 *CHAINID,
694 Some(*CHAINID),
695 )
696 .unwrap();
697
698 let (slot_a, _, s_header_1, s_header_2, _) = gen_block_headers_for_denunciation(None, None);
699 assert!(slot_a < slot);
700 let de_a = Denunciation::try_from((&s_header_1, &s_header_2, Some(*CHAINID))).unwrap();
701 let (slot_b, _, s_endo_1, s_endo_2, _) = gen_endorsements_for_denunciation(None, None);
702 assert!(slot_b < slot);
703 let de_b = Denunciation::try_from((&s_endo_1, &s_endo_2, Some(*CHAINID))).unwrap();
704
705 let block_header_1 = BlockHeader {
706 current_version: 0,
707 announced_version: None,
708 slot,
709 parents: parents_1,
710 operation_merkle_root: Hash::compute_from("mno".as_bytes()),
711 endorsements: vec![s_endorsement_1],
712 denunciations: vec![de_a, de_b],
713 };
714
715 let mut buffer = Vec::new();
716 let ser = BlockHeaderSerializer::new();
717 ser.serialize(&block_header_1, &mut buffer).unwrap();
718 let der = BlockHeaderDeserializer::new(
719 THREAD_COUNT,
720 ENDORSEMENT_COUNT,
721 MAX_DENUNCIATIONS_PER_BLOCK_HEADER,
722 None,
723 *CHAINID,
724 );
725
726 let (rem, block_header_der) = der.deserialize::<DeserializeError>(&buffer).unwrap();
727
728 assert!(rem.is_empty());
729 assert_eq!(block_header_1, block_header_der);
730 }
731
732 #[test]
733 fn test_block_header_no_parents_ser_der() {
734 let slot = Slot::new(0, 1);
735 let block_header_1 = BlockHeader {
736 current_version: 0,
737 announced_version: None,
738 slot,
739 parents: vec![],
740 operation_merkle_root: Hash::compute_from("mno".as_bytes()),
741 endorsements: vec![],
742 denunciations: vec![],
743 };
744
745 let mut buffer = Vec::new();
746 let ser = BlockHeaderSerializer::new();
747 ser.serialize(&block_header_1, &mut buffer).unwrap();
748 let der = BlockHeaderDeserializer::new(
749 THREAD_COUNT,
750 ENDORSEMENT_COUNT,
751 MAX_DENUNCIATIONS_PER_BLOCK_HEADER,
752 None,
753 *CHAINID,
754 );
755
756 let (rem, block_header_der) = der.deserialize::<DeserializeError>(&buffer).unwrap();
757
758 assert!(rem.is_empty());
759 assert_eq!(block_header_1, block_header_der);
760 }
761
762 #[test]
763 fn test_verify_sig_batch() {
764 let (_slot, _keypair, secured_header_1, secured_header_2, secured_header_3) =
765 gen_block_headers_for_denunciation(None, None);
766
767 let batch_1 = [(
769 secured_header_1.compute_signed_hash(Some(*CHAINID)),
770 secured_header_1.signature,
771 secured_header_1.content_creator_pub_key,
772 )];
773 verify_signature_batch(&batch_1).unwrap();
774
775 let batch_2 = [
777 (
778 secured_header_1.compute_signed_hash(Some(*CHAINID)),
779 secured_header_1.signature,
780 secured_header_1.content_creator_pub_key,
781 ),
782 (
783 secured_header_2.compute_signed_hash(Some(*CHAINID)),
784 secured_header_2.signature,
785 secured_header_2.content_creator_pub_key,
786 ),
787 (
788 secured_header_3.compute_signed_hash(Some(*CHAINID)),
789 secured_header_3.signature,
790 secured_header_3.content_creator_pub_key,
791 ),
792 ];
793 verify_signature_batch(&batch_2).unwrap();
794 }
795
796 #[test]
797 fn test_block_header_serde() {
798 let keypair = KeyPair::generate(0).unwrap();
799
800 let slot = Slot::new(7, 1);
801 let parents_1: Vec<BlockId> = (0..THREAD_COUNT)
802 .map(|i| BlockId::generate_from_hash(Hash::compute_from(&[i])))
803 .collect();
804
805 let endorsement_1 = Endorsement {
806 slot,
807 index: 1,
808 endorsed_block: parents_1[1],
809 };
810
811 assert_eq!(parents_1[1], endorsement_1.endorsed_block);
812
813 let s_endorsement_1: SecureShareEndorsement = Endorsement::new_verifiable(
814 endorsement_1,
815 EndorsementSerializer::new(),
816 &keypair,
817 *CHAINID,
818 Some(*CHAINID),
819 )
820 .unwrap();
821
822 let (slot_a, _, s_header_1, s_header_2, _) = gen_block_headers_for_denunciation(None, None);
823 assert!(slot_a < slot);
824 let de_a = Denunciation::try_from((&s_header_1, &s_header_2, Some(*CHAINID))).unwrap();
825 let (slot_b, _, s_endo_1, s_endo_2, _) = gen_endorsements_for_denunciation(None, None);
826 assert!(slot_b < slot);
827 let de_b = Denunciation::try_from((&s_endo_1, &s_endo_2, Some(*CHAINID))).unwrap();
828
829 let block_header_1 = BlockHeader {
830 current_version: 0,
831 announced_version: None,
832 slot,
833 parents: parents_1,
834 operation_merkle_root: Hash::compute_from("mno".as_bytes()),
835 endorsements: vec![s_endorsement_1],
836 denunciations: vec![de_a, de_b],
837 };
838
839 let serialized_block_header = serde_json::to_string(&block_header_1).unwrap();
840 let res_block_header: Value = serde_json::from_str(&serialized_block_header).unwrap();
841 assert_eq!(
843 block_header_1.current_version,
844 res_block_header["current_version"]
845 );
846 assert_eq!(
847 block_header_1.slot.period,
848 res_block_header["slot"]["period"]
849 );
850 assert_eq!(
851 block_header_1.slot.thread,
852 res_block_header["slot"]["thread"]
853 );
854 }
855}