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() {
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(&self, public_key: &PublicKey, content_hash: &Hash) -> Hash {
260 let mut signed_data: Vec<u8> = Vec::new();
261 signed_data.extend(public_key.to_bytes());
262 signed_data.extend(EndorsementDenunciationData::new(self.slot, self.index).to_bytes());
263 signed_data.extend(content_hash.to_bytes());
264 Hash::compute_from(&signed_data)
265 }
266}
267
268#[derive(Clone)]
270pub struct EndorsementSerializer {
271 slot_serializer: SlotSerializer,
272 u32_serializer: U32VarIntSerializer,
273 block_id_serializer: BlockIdSerializer,
274}
275
276impl EndorsementSerializer {
277 pub fn new() -> Self {
279 EndorsementSerializer {
280 slot_serializer: SlotSerializer::new(),
281 u32_serializer: U32VarIntSerializer::new(),
282 block_id_serializer: BlockIdSerializer::new(),
283 }
284 }
285}
286
287impl Default for EndorsementSerializer {
288 fn default() -> Self {
289 Self::new()
290 }
291}
292
293impl Serializer<Endorsement> for EndorsementSerializer {
294 fn serialize(&self, value: &Endorsement, buffer: &mut Vec<u8>) -> Result<(), SerializeError> {
309 self.slot_serializer.serialize(&value.slot, buffer)?;
310 self.u32_serializer.serialize(&value.index, buffer)?;
311 self.block_id_serializer
312 .serialize(&value.endorsed_block, buffer)?;
313 Ok(())
314 }
315}
316
317pub struct EndorsementDeserializer {
319 slot_deserializer: SlotDeserializer,
320 index_deserializer: U32VarIntDeserializer,
321 block_id_deserializer: BlockIdDeserializer,
322}
323
324impl EndorsementDeserializer {
325 pub fn new(thread_count: u8, endorsement_count: u32) -> Self {
327 EndorsementDeserializer {
328 slot_deserializer: SlotDeserializer::new(
329 (Included(0), Included(u64::MAX)),
330 (Included(0), Excluded(thread_count)),
331 ),
332 index_deserializer: U32VarIntDeserializer::new(
333 Included(0),
334 Excluded(endorsement_count),
335 ),
336 block_id_deserializer: BlockIdDeserializer::new(),
337 }
338 }
339}
340
341impl Deserializer<Endorsement> for EndorsementDeserializer {
342 fn deserialize<'a, E: ParseError<&'a [u8]> + ContextError<&'a [u8]>>(
362 &self,
363 buffer: &'a [u8],
364 ) -> IResult<&'a [u8], Endorsement, E> {
365 context(
366 "Failed endorsement deserialization",
367 tuple((
368 context("Failed slot deserialization", |input| {
369 self.slot_deserializer.deserialize(input)
370 }),
371 context("Failed index deserialization", |input| {
372 self.index_deserializer.deserialize(input)
373 }),
374 context("Failed endorsed_block deserialization", |input| {
375 self.block_id_deserializer.deserialize(input)
376 }),
377 )),
378 )
379 .map(|(slot, index, endorsed_block)| Endorsement {
380 slot,
381 index,
382 endorsed_block,
383 })
384 .parse(buffer)
385 }
386}
387
388pub struct EndorsementSerializerLW {
391 u32_serializer: U32VarIntSerializer,
393}
394
395impl EndorsementSerializerLW {
396 pub fn new() -> Self {
398 EndorsementSerializerLW {
399 u32_serializer: U32VarIntSerializer::new(),
400 }
401 }
402}
403
404impl Default for EndorsementSerializerLW {
405 fn default() -> Self {
406 Self::new()
407 }
408}
409
410impl Serializer<Endorsement> for EndorsementSerializerLW {
411 fn serialize(&self, value: &Endorsement, buffer: &mut Vec<u8>) -> Result<(), SerializeError> {
426 self.u32_serializer.serialize(&value.index, buffer)?;
427 Ok(())
428 }
429}
430
431pub struct EndorsementDeserializerLW {
433 index_deserializer: U32VarIntDeserializer,
434 slot: Slot,
435 endorsed_block: BlockId,
436}
437
438impl EndorsementDeserializerLW {
439 pub const fn new(endorsement_count: u32, slot: Slot, endorsed_block: BlockId) -> Self {
441 EndorsementDeserializerLW {
442 index_deserializer: U32VarIntDeserializer::new(
443 Included(0),
444 Excluded(endorsement_count),
445 ),
446 slot,
447 endorsed_block,
448 }
449 }
450}
451
452impl Deserializer<Endorsement> for EndorsementDeserializerLW {
453 fn deserialize<'a, E: ParseError<&'a [u8]> + ContextError<&'a [u8]>>(
473 &self,
474 buffer: &'a [u8],
475 ) -> IResult<&'a [u8], Endorsement, E> {
476 context("Failed index deserialization", |input| {
477 self.index_deserializer.deserialize(input)
478 })
479 .map(|index| Endorsement {
480 slot: self.slot,
481 index,
482 endorsed_block: self.endorsed_block,
483 })
484 .parse(buffer)
485 }
486}
487
488#[derive(Debug)]
490pub struct EndorsementDenunciationData {
491 slot: Slot,
492 index: u32,
493}
494
495impl EndorsementDenunciationData {
496 pub fn new(slot: Slot, index: u32) -> Self {
498 Self { slot, index }
499 }
500
501 pub fn to_bytes(&self) -> Vec<u8> {
503 let mut buf = Vec::new();
504 buf.extend(self.slot.to_bytes_key());
505 buf.extend(self.index.to_le_bytes());
506 buf
507 }
508}
509
510#[cfg(test)]
511mod tests {
512 use crate::secure_share::{SecureShareContent, SecureShareDeserializer, SecureShareSerializer};
513 use massa_signature::verify_signature_batch;
514 use serde_json::Value;
515
516 use super::*;
517 use crate::config::CHAINID;
518 use massa_serialization::DeserializeError;
519 use massa_signature::KeyPair;
520 use serial_test::serial;
521
522 #[test]
523 #[serial]
524 fn test_endorsement_serialization() {
525 let sender_keypair = KeyPair::generate(0).unwrap();
526 let content = Endorsement {
527 slot: Slot::new(10, 1),
528 index: 0,
529 endorsed_block: BlockId::generate_from_hash(Hash::compute_from("blk".as_bytes())),
530 };
531 let endorsement: SecureShareEndorsement = Endorsement::new_verifiable(
532 content,
533 EndorsementSerializer::new(),
534 &sender_keypair,
535 *CHAINID,
536 )
537 .unwrap();
538
539 let mut ser_endorsement: Vec<u8> = Vec::new();
540 let serializer = SecureShareSerializer::new();
541 serializer
542 .serialize(&endorsement, &mut ser_endorsement)
543 .unwrap();
544 let (_, res_endorsement): (&[u8], SecureShareEndorsement) =
545 SecureShareDeserializer::new(EndorsementDeserializer::new(32, 1), *CHAINID)
546 .deserialize::<DeserializeError>(&ser_endorsement)
547 .unwrap();
548 assert_eq!(res_endorsement, endorsement);
549 }
550
551 #[test]
552 #[serial]
553 fn test_endorsement_lightweight_serialization() {
554 let sender_keypair = KeyPair::generate(0).unwrap();
555 let content = Endorsement {
556 slot: Slot::new(10, 1),
557 index: 0,
558 endorsed_block: BlockId::generate_from_hash(Hash::compute_from("blk".as_bytes())),
559 };
560 let endorsement: SecureShareEndorsement = Endorsement::new_verifiable(
561 content,
562 EndorsementSerializerLW::new(),
563 &sender_keypair,
564 *CHAINID,
565 )
566 .unwrap();
567
568 let mut ser_endorsement: Vec<u8> = Vec::new();
569 let serializer = SecureShareSerializer::new();
570 serializer
571 .serialize(&endorsement, &mut ser_endorsement)
572 .unwrap();
573
574 let parent = BlockId::generate_from_hash(Hash::compute_from("blk".as_bytes()));
575
576 let (_, res_endorsement): (&[u8], SecureShareEndorsement) = SecureShareDeserializer::new(
577 EndorsementDeserializerLW::new(1, Slot::new(10, 1), parent),
578 *CHAINID,
579 )
580 .deserialize::<DeserializeError>(&ser_endorsement)
581 .unwrap();
582 assert_eq!(res_endorsement.content.index, endorsement.content.index);
584 }
585
586 #[test]
587 fn test_verify_sig_batch() {
588 let sender_keypair = KeyPair::generate(0).unwrap();
591 let content_1 = Endorsement {
592 slot: Slot::new(10, 1),
593 index: 0,
594 endorsed_block: BlockId::generate_from_hash(Hash::compute_from("blk1".as_bytes())),
595 };
596 let s_endorsement_1: SecureShareEndorsement = Endorsement::new_verifiable(
597 content_1,
598 EndorsementSerializer::new(),
599 &sender_keypair,
600 *CHAINID,
601 )
602 .unwrap();
603 let mut serialized = vec![];
604 SecureShareSerializer::new()
605 .serialize(&s_endorsement_1, &mut serialized)
606 .unwrap();
607 let (_, s_endorsement_1): (&[u8], SecureShare<Endorsement, EndorsementId>) =
608 SecureShareDeserializer::new(EndorsementDeserializer::new(32, 32), *CHAINID)
609 .deserialize::<DeserializeError>(&serialized)
610 .unwrap();
611 let sender_keypair = KeyPair::generate(0).unwrap();
612 let content_2 = Endorsement {
613 slot: Slot::new(2, 5),
614 index: 0,
615 endorsed_block: BlockId::generate_from_hash(Hash::compute_from("blk2".as_bytes())),
616 };
617 let s_endorsement_2: SecureShareEndorsement = Endorsement::new_verifiable(
618 content_2,
619 EndorsementSerializerLW::new(),
620 &sender_keypair,
621 *CHAINID,
622 )
623 .unwrap();
624
625 let batch_1 = [(
627 s_endorsement_1.compute_signed_hash(),
628 s_endorsement_1.signature,
629 s_endorsement_1.content_creator_pub_key,
630 )];
631 verify_signature_batch(&batch_1).unwrap();
632
633 let batch_2 = [
635 (
636 s_endorsement_1.compute_signed_hash(),
637 s_endorsement_1.signature,
638 s_endorsement_1.content_creator_pub_key,
639 ),
640 (
641 s_endorsement_2.compute_signed_hash(),
642 s_endorsement_2.signature,
643 s_endorsement_2.content_creator_pub_key,
644 ),
645 ];
646 verify_signature_batch(&batch_2).unwrap();
647 }
648
649 #[test]
650 #[serial]
651 fn test_endorsement_id() {
652 let expected_endorsement_id =
653 EndorsementId::from_str("E12Uy7hrAUHpmHQTWu68p17v7VtZJ6syBTWEJH6jwMTWJB6fdSc7")
654 .unwrap();
655 let actual_endorsement_id = EndorsementId::new(Hash::compute_from("edm".as_bytes()));
656
657 assert_eq!(actual_endorsement_id, expected_endorsement_id);
658 }
659
660 #[test]
661 #[serial]
662 fn test_endorsement_id_errors() {
663 let actual_error = EndorsementId::from_str("SomeInvalidEndorsementId")
664 .unwrap_err()
665 .to_string();
666 let expected_error = "endorsement id parsing error".to_string();
667
668 assert_eq!(actual_error, expected_error);
669 }
670
671 #[test]
672 #[serial]
673 fn test_endorsement_serde() {
674 let orig_endorsement = Endorsement {
675 slot: Slot::new(10, 1),
676 index: 0,
677 endorsed_block: BlockId::generate_from_hash(Hash::compute_from("blk".as_bytes())),
678 };
679 let serialized_endorsement = serde_json::to_string(&orig_endorsement).unwrap();
680 let res_endorsement: Value = serde_json::from_str(&serialized_endorsement).unwrap();
681 assert_eq!(
683 orig_endorsement.slot.period,
684 res_endorsement["slot"]["period"]
685 );
686 assert_eq!(
687 orig_endorsement.slot.thread,
688 res_endorsement["slot"]["thread"]
689 );
690 assert_eq!(orig_endorsement.index, res_endorsement["index"]);
691 assert_eq!(
692 orig_endorsement.endorsed_block.to_string(),
693 res_endorsement["endorsed_block"]
694 );
695 }
696}