massa_models/
clique.rs

1// Copyright (c) 2022 MASSA LABS <info@massa.net>
2
3use massa_serialization::{
4    Deserializer, SerializeError, Serializer, U32VarIntDeserializer, U32VarIntSerializer,
5    U64VarIntDeserializer, U64VarIntSerializer,
6};
7use nom::branch::alt;
8use nom::bytes::complete::tag;
9use nom::combinator::value;
10use nom::error::context;
11use nom::error::{ContextError, ParseError};
12use nom::multi::length_count;
13use nom::sequence::tuple;
14use nom::{IResult, Parser};
15use serde::{Deserialize, Serialize};
16
17use crate::block_id::{BlockId, BlockIdDeserializer, BlockIdSerializer};
18use crate::prehash::PreHashSet;
19use std::ops::Bound::{Excluded, Included};
20
21/// Mutually compatible blocks in the graph
22#[derive(Clone, Debug, Deserialize, Serialize)]
23pub struct Clique {
24    /// the block ids of the blocks in that clique
25    pub block_ids: PreHashSet<BlockId>,
26    /// Fitness used to compute finality
27    /// Depends on descendants and endorsement count
28    pub fitness: u64,
29    /// True if it is the clique of higher fitness
30    pub is_blockclique: bool,
31}
32
33impl Default for Clique {
34    fn default() -> Self {
35        Clique {
36            block_ids: Default::default(),
37            fitness: 0,
38            is_blockclique: true,
39        }
40    }
41}
42
43/// Basic serializer for `Clique`
44#[derive(Default)]
45pub struct CliqueSerializer {
46    block_ids_length_serializer: U32VarIntSerializer,
47    fitness_serializer: U64VarIntSerializer,
48    block_id_serializer: BlockIdSerializer,
49}
50
51impl CliqueSerializer {
52    /// Creates a `CliqueSerializer`
53    pub fn new() -> Self {
54        Self {
55            block_ids_length_serializer: U32VarIntSerializer::new(),
56            fitness_serializer: U64VarIntSerializer::new(),
57            block_id_serializer: BlockIdSerializer::new(),
58        }
59    }
60}
61
62impl Serializer<Clique> for CliqueSerializer {
63    /// ## Example
64    /// ```rust
65    /// # use massa_models::clique::{Clique, CliqueSerializer};
66    /// # use massa_models::block_id::BlockId;
67    /// # use massa_hash::Hash;
68    /// # use std::str::FromStr;
69    /// # use massa_serialization::Serializer;
70    /// # pub fn get_dummy_block_id(s: &str) -> BlockId {
71    /// #     BlockId::generate_from_hash(Hash::compute_from(s.as_bytes()))
72    /// # }
73    /// let clique = Clique {
74    ///         block_ids: vec![get_dummy_block_id("parent1"), get_dummy_block_id("parent2")].into_iter().collect(),
75    ///         fitness: 123,
76    ///         is_blockclique: true,
77    ///     };
78    /// let mut buffer = Vec::new();
79    /// let mut serializer = CliqueSerializer::new();
80    /// serializer.serialize(&clique, &mut buffer).unwrap();
81    /// ```
82    fn serialize(&self, value: &Clique, buffer: &mut Vec<u8>) -> Result<(), SerializeError> {
83        self.block_ids_length_serializer
84            .serialize(&(value.block_ids.len() as u32), buffer)?;
85        for block_id in &value.block_ids {
86            self.block_id_serializer.serialize(block_id, buffer)?;
87        }
88        self.fitness_serializer.serialize(&value.fitness, buffer)?;
89        buffer.push(u8::from(value.is_blockclique));
90        Ok(())
91    }
92}
93
94/// Basic deserializer for `Clique`
95pub struct CliqueDeserializer {
96    block_ids_length_deserializer: U32VarIntDeserializer,
97    block_id_deserializer: BlockIdDeserializer,
98    fitness_deserializer: U64VarIntDeserializer,
99}
100
101impl CliqueDeserializer {
102    /// Creates a `CliqueDeserializer`
103    pub fn new(max_bootstrap_blocks: u32) -> Self {
104        Self {
105            block_ids_length_deserializer: U32VarIntDeserializer::new(
106                Included(0),
107                Excluded(max_bootstrap_blocks),
108            ),
109            block_id_deserializer: BlockIdDeserializer::new(),
110            fitness_deserializer: U64VarIntDeserializer::new(Included(0), Included(u64::MAX)),
111        }
112    }
113}
114
115impl Deserializer<Clique> for CliqueDeserializer {
116    /// ## Example
117    /// ```rust
118    /// # use massa_models::clique::{Clique, CliqueDeserializer, CliqueSerializer};
119    /// # use massa_models::block_id::BlockId;
120    /// # use massa_hash::Hash;
121    /// # use std::str::FromStr;
122    /// # use massa_serialization::{Serializer, Deserializer, DeserializeError};
123    /// # pub fn get_dummy_block_id(s: &str) -> BlockId {
124    /// #     BlockId::generate_from_hash(Hash::compute_from(s.as_bytes()))
125    /// # }
126    /// let clique = Clique {
127    ///         block_ids: vec![get_dummy_block_id("parent1"), get_dummy_block_id("parent2")].into_iter().collect(),
128    ///         fitness: 123,
129    ///         is_blockclique: true,
130    ///     };
131    /// let mut buffer = Vec::new();
132    /// let mut serializer = CliqueSerializer::new();
133    /// serializer.serialize(&clique, &mut buffer).unwrap();
134    /// let mut deserializer = CliqueDeserializer::new(1000);
135    /// let (rest, clique_deserialized) = deserializer.deserialize::<DeserializeError>(&buffer).unwrap();
136    /// assert_eq!(clique.block_ids, clique_deserialized.block_ids);
137    /// assert_eq!(clique.is_blockclique, clique_deserialized.is_blockclique);
138    /// assert_eq!(clique.fitness, clique_deserialized.fitness);
139    /// ```
140    fn deserialize<'a, E: ParseError<&'a [u8]> + ContextError<&'a [u8]>>(
141        &self,
142        buffer: &'a [u8],
143    ) -> IResult<&'a [u8], Clique, E> {
144        context(
145            "Failed Clique deserialization",
146            tuple((
147                length_count(
148                    context("Failed length deserialization", |input| {
149                        self.block_ids_length_deserializer.deserialize(input)
150                    }),
151                    context("Failed block_id deserialization", |input| {
152                        self.block_id_deserializer.deserialize(input)
153                    }),
154                ),
155                context("Failed fitness deserialization", |input| {
156                    self.fitness_deserializer.deserialize(input)
157                }),
158                context(
159                    "Failed is_blockclique deserialization",
160                    alt((
161                        value(true, |input| tag(&[1u8])(input)),
162                        value(false, |input| tag(&[0u8])(input)),
163                    )),
164                ),
165            )),
166        )
167        .map(|(block_ids, fitness, is_blockclique)| Clique {
168            block_ids: block_ids.into_iter().collect(),
169            fitness,
170            is_blockclique,
171        })
172        .parse(buffer)
173    }
174}