1use crate::block_id::{BlockIdDeserializer, BlockIdSerializer};
4use crate::prehash::PreHashed;
5use crate::secure_share::{Id, SecureShare, SecureShareContent};
6use crate::slot::{Slot, SlotDeserializer, SlotSerializer};
7use crate::{block_id::BlockId, error::ModelsError};
8use massa_hash::{Hash, HashDeserializer};
9use massa_serialization::{
10 DeserializeError, Deserializer, SerializeError, Serializer, U32VarIntDeserializer,
11 U32VarIntSerializer, U64VarIntDeserializer, U64VarIntSerializer,
12};
13use massa_signature::PublicKey;
14use nom::error::{context, ErrorKind};
15use nom::sequence::tuple;
16use nom::Parser;
17use nom::{
18 error::{ContextError, ParseError},
19 IResult,
20};
21use serde::{Deserialize, Serialize};
22use serde_with::{DeserializeFromStr, SerializeDisplay};
23use std::fmt::Formatter;
24use std::ops::Bound::{Excluded, Included};
25use std::{fmt::Display, str::FromStr};
26use transition::Versioned;
27
28pub const ENDORSEMENT_ID_SIZE_BYTES: usize = massa_hash::HASH_SIZE_BYTES;
30
31#[allow(missing_docs)]
33#[transition::versioned(versions("0"))]
34#[derive(
35 Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, SerializeDisplay, DeserializeFromStr,
36)]
37pub struct EndorsementId(Hash);
38
39const ENDORSEMENTID_PREFIX: char = 'E';
40const ENDORSEMENTID_VERSION: u64 = 0;
41
42impl PreHashed for EndorsementId {}
43
44impl Id for EndorsementId {
45 fn new(hash: Hash) -> Self {
46 EndorsementId::EndorsementIdV0(EndorsementIdV0(hash))
47 }
48
49 fn get_hash(&self) -> &Hash {
50 match self {
51 EndorsementId::EndorsementIdV0(endorsement_id) => endorsement_id.get_hash(),
52 }
53 }
54}
55
56#[transition::impl_version(versions("0"))]
57impl EndorsementId {
58 fn get_hash(&self) -> &Hash {
59 &self.0
60 }
61}
62
63impl std::fmt::Display for EndorsementId {
64 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
65 match self {
66 EndorsementId::EndorsementIdV0(id) => write!(f, "{}", id),
67 }
68 }
69}
70
71#[transition::impl_version(versions("0"))]
72impl std::fmt::Display for EndorsementId {
73 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
74 let u64_serializer = U64VarIntSerializer::new();
75 let mut bytes: Vec<u8> = Vec::new();
77 u64_serializer
78 .serialize(&ENDORSEMENTID_VERSION, &mut bytes)
79 .map_err(|_| std::fmt::Error)?;
80 bytes.extend(self.0.to_bytes());
81 write!(
82 f,
83 "{}{}",
84 ENDORSEMENTID_PREFIX,
85 bs58::encode(bytes).with_check().into_string()
86 )
87 }
88}
89
90impl std::fmt::Debug for EndorsementId {
91 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
92 write!(f, "{}", self)
93 }
94}
95
96impl FromStr for EndorsementId {
97 type Err = ModelsError;
98 fn from_str(s: &str) -> Result<Self, Self::Err> {
110 let mut chars = s.chars();
111 match chars.next() {
112 Some(prefix) if prefix == ENDORSEMENTID_PREFIX => {
113 let data = chars.collect::<String>();
114 let decoded_bs58_check = bs58::decode(data)
115 .with_check(None)
116 .into_vec()
117 .map_err(|_| ModelsError::EndorsementIdParseError)?;
118 let endorsement_id_deserializer = EndorsementIdDeserializer::new();
119 let (rest, endorsement_id) = endorsement_id_deserializer
120 .deserialize::<DeserializeError>(&decoded_bs58_check[..])
121 .map_err(|_| ModelsError::EndorsementIdParseError)?;
122 if rest.is_empty() {
123 Ok(endorsement_id)
124 } else {
125 Err(ModelsError::EndorsementIdParseError)
126 }
127 }
128 _ => Err(ModelsError::EndorsementIdParseError),
129 }
130 }
131}
132
133#[transition::impl_version(versions("0"))]
134impl FromStr for EndorsementId {
135 type Err = ModelsError;
136 fn from_str(s: &str) -> Result<Self, Self::Err> {
137 let mut chars = s.chars();
138 match chars.next() {
139 Some(prefix) if prefix == ENDORSEMENTID_PREFIX => {
140 let data = chars.collect::<String>();
141 let decoded_bs58_check = bs58::decode(data)
142 .with_check(None)
143 .into_vec()
144 .map_err(|_| ModelsError::EndorsementIdParseError)?;
145 let endorsement_id_deserializer = EndorsementIdDeserializer::new();
146 let (rest, endorsement_id) = endorsement_id_deserializer
147 .deserialize::<DeserializeError>(&decoded_bs58_check[..])
148 .map_err(|_| ModelsError::EndorsementIdParseError)?;
149 if rest.is_empty() {
150 Ok(endorsement_id)
151 } else {
152 Err(ModelsError::EndorsementIdParseError)
153 }
154 }
155 _ => Err(ModelsError::EndorsementIdParseError),
156 }
157 }
158}
159
160struct EndorsementIdDeserializer {
161 version_deserializer: U64VarIntDeserializer,
162 hash_deserializer: HashDeserializer,
163}
164
165impl EndorsementIdDeserializer {
166 pub fn new() -> Self {
167 Self {
168 version_deserializer: U64VarIntDeserializer::new(Included(0), Included(u64::MAX)),
169 hash_deserializer: HashDeserializer::new(),
170 }
171 }
172}
173
174impl Deserializer<EndorsementId> for EndorsementIdDeserializer {
175 fn deserialize<'a, E: ParseError<&'a [u8]> + ContextError<&'a [u8]>>(
176 &self,
177 buffer: &'a [u8],
178 ) -> IResult<&'a [u8], EndorsementId, E> {
179 if buffer.len() < 2 {
181 return Err(nom::Err::Error(E::from_error_kind(buffer, ErrorKind::Eof)));
182 }
183 let (rest, endorsement_id_version) = self
184 .version_deserializer
185 .deserialize(buffer)
186 .map_err(|_: nom::Err<E>| {
187 nom::Err::Error(E::from_error_kind(buffer, ErrorKind::Eof))
188 })?;
189 match endorsement_id_version {
190 <EndorsementId!["0"]>::VERSION => {
191 let (rest, endorsement_id) = self.deserialize(rest)?;
192 Ok((rest, EndorsementIdVariant))
193 }
194 _ => Err(nom::Err::Error(E::from_error_kind(buffer, ErrorKind::Eof))),
195 }
196 }
197}
198
199#[transition::impl_version(versions("0"), structures("EndorsementId"))]
200impl Deserializer<EndorsementId> for EndorsementIdDeserializer {
201 fn deserialize<'a, E: ParseError<&'a [u8]> + ContextError<&'a [u8]>>(
202 &self,
203 buffer: &'a [u8],
204 ) -> IResult<&'a [u8], EndorsementId, E> {
205 context("Failed OperationId deserialization", |input| {
206 self.hash_deserializer.deserialize(input)
207 })
208 .map(EndorsementId)
209 .parse(buffer)
210 }
211}
212impl Display for Endorsement {
213 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
214 writeln!(
215 f,
216 "Endorsed block: {} at slot {}",
217 self.endorsed_block, self.slot
218 )?;
219 writeln!(f, "Index: {}", self.index)?;
220 Ok(())
221 }
222}
223
224#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
226pub struct Endorsement {
227 pub slot: Slot,
229 pub index: u32,
231 pub endorsed_block: BlockId,
234}
235
236#[cfg(any(test, feature = "test-exports"))]
237impl SecureShareEndorsement {
238 pub fn check_invariants(&self) -> Result<(), Box<dyn std::error::Error>> {
241 if let Err(e) = self.verify_signature(Some(*crate::config::CHAINID)) {
242 return Err(e.into());
243 }
244 if self.content.slot.thread >= crate::config::THREAD_COUNT {
245 Err("Endorsement slot on non-existent thread".into())
246 } else if self.content.index >= crate::config::ENDORSEMENT_COUNT {
247 Err("Endorsement index out of range".into())
248 } else {
249 Ok(())
250 }
251 }
252}
253
254pub type SecureShareEndorsement = SecureShare<Endorsement, EndorsementId>;
256
257impl SecureShareContent for Endorsement {
258 fn compute_signed_hash(
266 &self,
267 public_key: &PublicKey,
268 content_hash: &Hash,
269 sig_chain_id: Option<u64>,
270 ) -> Hash {
271 let mut signed_data: Vec<u8> = Vec::new();
272 signed_data.extend(public_key.to_bytes());
273 signed_data.extend(
274 EndorsementDenunciationData::new(self.slot, self.index, sig_chain_id).to_bytes(),
275 );
276 signed_data.extend(content_hash.to_bytes());
277 Hash::compute_from(&signed_data)
278 }
279}
280
281#[derive(Clone)]
283pub struct EndorsementSerializer {
284 slot_serializer: SlotSerializer,
285 u32_serializer: U32VarIntSerializer,
286 block_id_serializer: BlockIdSerializer,
287}
288
289impl EndorsementSerializer {
290 pub fn new() -> Self {
292 EndorsementSerializer {
293 slot_serializer: SlotSerializer::new(),
294 u32_serializer: U32VarIntSerializer::new(),
295 block_id_serializer: BlockIdSerializer::new(),
296 }
297 }
298}
299
300impl Default for EndorsementSerializer {
301 fn default() -> Self {
302 Self::new()
303 }
304}
305
306impl Serializer<Endorsement> for EndorsementSerializer {
307 fn serialize(&self, value: &Endorsement, buffer: &mut Vec<u8>) -> Result<(), SerializeError> {
322 self.slot_serializer.serialize(&value.slot, buffer)?;
323 self.u32_serializer.serialize(&value.index, buffer)?;
324 self.block_id_serializer
325 .serialize(&value.endorsed_block, buffer)?;
326 Ok(())
327 }
328}
329
330pub struct EndorsementDeserializer {
332 slot_deserializer: SlotDeserializer,
333 index_deserializer: U32VarIntDeserializer,
334 block_id_deserializer: BlockIdDeserializer,
335}
336
337impl EndorsementDeserializer {
338 pub fn new(thread_count: u8, endorsement_count: u32) -> Self {
340 EndorsementDeserializer {
341 slot_deserializer: SlotDeserializer::new(
342 (Included(0), Included(u64::MAX)),
343 (Included(0), Excluded(thread_count)),
344 ),
345 index_deserializer: U32VarIntDeserializer::new(
346 Included(0),
347 Excluded(endorsement_count),
348 ),
349 block_id_deserializer: BlockIdDeserializer::new(),
350 }
351 }
352}
353
354impl Deserializer<Endorsement> for EndorsementDeserializer {
355 fn deserialize<'a, E: ParseError<&'a [u8]> + ContextError<&'a [u8]>>(
375 &self,
376 buffer: &'a [u8],
377 ) -> IResult<&'a [u8], Endorsement, E> {
378 context(
379 "Failed endorsement deserialization",
380 tuple((
381 context("Failed slot deserialization", |input| {
382 self.slot_deserializer.deserialize(input)
383 }),
384 context("Failed index deserialization", |input| {
385 self.index_deserializer.deserialize(input)
386 }),
387 context("Failed endorsed_block deserialization", |input| {
388 self.block_id_deserializer.deserialize(input)
389 }),
390 )),
391 )
392 .map(|(slot, index, endorsed_block)| Endorsement {
393 slot,
394 index,
395 endorsed_block,
396 })
397 .parse(buffer)
398 }
399}
400
401pub struct EndorsementSerializerLW {
404 u32_serializer: U32VarIntSerializer,
406}
407
408impl EndorsementSerializerLW {
409 pub fn new() -> Self {
411 EndorsementSerializerLW {
412 u32_serializer: U32VarIntSerializer::new(),
413 }
414 }
415}
416
417impl Default for EndorsementSerializerLW {
418 fn default() -> Self {
419 Self::new()
420 }
421}
422
423impl Serializer<Endorsement> for EndorsementSerializerLW {
424 fn serialize(&self, value: &Endorsement, buffer: &mut Vec<u8>) -> Result<(), SerializeError> {
439 self.u32_serializer.serialize(&value.index, buffer)?;
440 Ok(())
441 }
442}
443
444pub struct EndorsementDeserializerLW {
446 index_deserializer: U32VarIntDeserializer,
447 slot: Slot,
448 endorsed_block: BlockId,
449}
450
451impl EndorsementDeserializerLW {
452 pub const fn new(endorsement_count: u32, slot: Slot, endorsed_block: BlockId) -> Self {
454 EndorsementDeserializerLW {
455 index_deserializer: U32VarIntDeserializer::new(
456 Included(0),
457 Excluded(endorsement_count),
458 ),
459 slot,
460 endorsed_block,
461 }
462 }
463}
464
465impl Deserializer<Endorsement> for EndorsementDeserializerLW {
466 fn deserialize<'a, E: ParseError<&'a [u8]> + ContextError<&'a [u8]>>(
486 &self,
487 buffer: &'a [u8],
488 ) -> IResult<&'a [u8], Endorsement, E> {
489 context("Failed index deserialization", |input| {
490 self.index_deserializer.deserialize(input)
491 })
492 .map(|index| Endorsement {
493 slot: self.slot,
494 index,
495 endorsed_block: self.endorsed_block,
496 })
497 .parse(buffer)
498 }
499}
500
501#[derive(Debug)]
503pub struct EndorsementDenunciationData {
504 slot: Slot,
505 index: u32,
506 chain_id: Option<u64>,
509}
510
511impl EndorsementDenunciationData {
512 pub fn new(slot: Slot, index: u32, chain_id: Option<u64>) -> Self {
515 Self {
516 slot,
517 index,
518 chain_id,
519 }
520 }
521
522 #[allow(dead_code)]
526 pub fn new_legacy(slot: Slot, index: u32) -> Self {
527 Self {
528 slot,
529 index,
530 chain_id: None,
531 }
532 }
533
534 pub fn to_bytes(&self) -> Vec<u8> {
536 let mut buf = Vec::new();
537 buf.extend(self.slot.to_bytes_key());
538 buf.extend(self.index.to_le_bytes());
539 if let Some(chain_id) = self.chain_id {
540 buf.extend(chain_id.to_be_bytes());
541 }
542 buf
543 }
544}
545
546#[cfg(test)]
547mod tests {
548 use crate::secure_share::{SecureShareContent, SecureShareDeserializer, SecureShareSerializer};
549 use massa_signature::verify_signature_batch;
550 use serde_json::Value;
551
552 use super::*;
553 use crate::config::CHAINID;
554 use massa_serialization::DeserializeError;
555 use massa_signature::KeyPair;
556 use serial_test::serial;
557
558 #[test]
559 #[serial]
560 fn test_endorsement_serialization() {
561 let sender_keypair = KeyPair::generate(0).unwrap();
562 let content = Endorsement {
563 slot: Slot::new(10, 1),
564 index: 0,
565 endorsed_block: BlockId::generate_from_hash(Hash::compute_from("blk".as_bytes())),
566 };
567 let endorsement: SecureShareEndorsement = Endorsement::new_verifiable(
568 content,
569 EndorsementSerializer::new(),
570 &sender_keypair,
571 *CHAINID,
572 Some(*CHAINID),
573 )
574 .unwrap();
575
576 let mut ser_endorsement: Vec<u8> = Vec::new();
577 let serializer = SecureShareSerializer::new();
578 serializer
579 .serialize(&endorsement, &mut ser_endorsement)
580 .unwrap();
581 let (_, res_endorsement): (&[u8], SecureShareEndorsement) =
582 SecureShareDeserializer::new(EndorsementDeserializer::new(32, 1), *CHAINID)
583 .deserialize::<DeserializeError>(&ser_endorsement)
584 .unwrap();
585 assert_eq!(res_endorsement, endorsement);
586 }
587
588 #[test]
589 #[serial]
590 fn test_endorsement_lightweight_serialization() {
591 let sender_keypair = KeyPair::generate(0).unwrap();
592 let content = Endorsement {
593 slot: Slot::new(10, 1),
594 index: 0,
595 endorsed_block: BlockId::generate_from_hash(Hash::compute_from("blk".as_bytes())),
596 };
597 let endorsement: SecureShareEndorsement = Endorsement::new_verifiable(
598 content,
599 EndorsementSerializerLW::new(),
600 &sender_keypair,
601 *CHAINID,
602 Some(*CHAINID),
603 )
604 .unwrap();
605
606 let mut ser_endorsement: Vec<u8> = Vec::new();
607 let serializer = SecureShareSerializer::new();
608 serializer
609 .serialize(&endorsement, &mut ser_endorsement)
610 .unwrap();
611
612 let parent = BlockId::generate_from_hash(Hash::compute_from("blk".as_bytes()));
613
614 let (_, res_endorsement): (&[u8], SecureShareEndorsement) = SecureShareDeserializer::new(
615 EndorsementDeserializerLW::new(1, Slot::new(10, 1), parent),
616 *CHAINID,
617 )
618 .deserialize::<DeserializeError>(&ser_endorsement)
619 .unwrap();
620 assert_eq!(res_endorsement.content.index, endorsement.content.index);
622 }
623
624 #[test]
625 fn test_verify_sig_batch() {
626 let sender_keypair = KeyPair::generate(0).unwrap();
629 let content_1 = Endorsement {
630 slot: Slot::new(10, 1),
631 index: 0,
632 endorsed_block: BlockId::generate_from_hash(Hash::compute_from("blk1".as_bytes())),
633 };
634 let s_endorsement_1: SecureShareEndorsement = Endorsement::new_verifiable(
635 content_1,
636 EndorsementSerializer::new(),
637 &sender_keypair,
638 *CHAINID,
639 Some(*CHAINID),
640 )
641 .unwrap();
642 let mut serialized = vec![];
643 SecureShareSerializer::new()
644 .serialize(&s_endorsement_1, &mut serialized)
645 .unwrap();
646 let (_, s_endorsement_1): (&[u8], SecureShare<Endorsement, EndorsementId>) =
647 SecureShareDeserializer::new(EndorsementDeserializer::new(32, 32), *CHAINID)
648 .deserialize::<DeserializeError>(&serialized)
649 .unwrap();
650 let sender_keypair = KeyPair::generate(0).unwrap();
651 let content_2 = Endorsement {
652 slot: Slot::new(2, 5),
653 index: 0,
654 endorsed_block: BlockId::generate_from_hash(Hash::compute_from("blk2".as_bytes())),
655 };
656 let s_endorsement_2: SecureShareEndorsement = Endorsement::new_verifiable(
657 content_2,
658 EndorsementSerializerLW::new(),
659 &sender_keypair,
660 *CHAINID,
661 Some(*CHAINID),
662 )
663 .unwrap();
664
665 let batch_1 = [(
667 s_endorsement_1.compute_signed_hash(Some(*CHAINID)),
668 s_endorsement_1.signature,
669 s_endorsement_1.content_creator_pub_key,
670 )];
671 verify_signature_batch(&batch_1).unwrap();
672
673 let batch_2 = [
675 (
676 s_endorsement_1.compute_signed_hash(Some(*CHAINID)),
677 s_endorsement_1.signature,
678 s_endorsement_1.content_creator_pub_key,
679 ),
680 (
681 s_endorsement_2.compute_signed_hash(Some(*CHAINID)),
682 s_endorsement_2.signature,
683 s_endorsement_2.content_creator_pub_key,
684 ),
685 ];
686 verify_signature_batch(&batch_2).unwrap();
687 }
688
689 #[test]
690 #[serial]
691 fn test_endorsement_id() {
692 let expected_endorsement_id =
693 EndorsementId::from_str("E12Uy7hrAUHpmHQTWu68p17v7VtZJ6syBTWEJH6jwMTWJB6fdSc7")
694 .unwrap();
695 let actual_endorsement_id = EndorsementId::new(Hash::compute_from("edm".as_bytes()));
696
697 assert_eq!(actual_endorsement_id, expected_endorsement_id);
698 }
699
700 #[test]
701 #[serial]
702 fn test_endorsement_id_errors() {
703 let actual_error = EndorsementId::from_str("SomeInvalidEndorsementId")
704 .unwrap_err()
705 .to_string();
706 let expected_error = "endorsement id parsing error".to_string();
707
708 assert_eq!(actual_error, expected_error);
709 }
710
711 #[test]
712 #[serial]
713 fn test_endorsement_serde() {
714 let orig_endorsement = Endorsement {
715 slot: Slot::new(10, 1),
716 index: 0,
717 endorsed_block: BlockId::generate_from_hash(Hash::compute_from("blk".as_bytes())),
718 };
719 let serialized_endorsement = serde_json::to_string(&orig_endorsement).unwrap();
720 let res_endorsement: Value = serde_json::from_str(&serialized_endorsement).unwrap();
721 assert_eq!(
723 orig_endorsement.slot.period,
724 res_endorsement["slot"]["period"]
725 );
726 assert_eq!(
727 orig_endorsement.slot.thread,
728 res_endorsement["slot"]["thread"]
729 );
730 assert_eq!(orig_endorsement.index, res_endorsement["index"]);
731 assert_eq!(
732 orig_endorsement.endorsed_block.to_string(),
733 res_endorsement["endorsed_block"]
734 );
735 }
736}