massa_pos_exports/
cycle_info.rs

1use bitvec::vec::BitVec;
2use massa_hash::{HashXof, HashXofDeserializer, HashXofSerializer, HASH_XOF_SIZE_BYTES};
3use massa_models::{
4    address::{Address, AddressDeserializer, AddressSerializer},
5    prehash::PreHashMap,
6    serialization::{BitVecDeserializer, BitVecSerializer},
7};
8use massa_serialization::{
9    Deserializer, OptionDeserializer, OptionSerializer, SerializeError, Serializer,
10    U64VarIntDeserializer, U64VarIntSerializer,
11};
12use nom::{
13    branch::alt,
14    bytes::complete::tag,
15    combinator::value,
16    error::{context, ContextError, ParseError},
17    multi::length_count,
18    sequence::tuple,
19    IResult, Parser,
20};
21use num::rational::Ratio;
22use serde::{Deserialize, Serialize};
23use std::collections::BTreeMap;
24use std::ops::Bound::Included;
25
26/// State of a cycle for all threads
27#[derive(Debug, Clone, PartialEq, Eq)]
28pub struct CycleInfo {
29    /// cycle number
30    pub cycle: u64,
31    /// whether the cycle is complete (all slots final)
32    pub complete: bool,
33    /// number of rolls each staking address has
34    pub roll_counts: BTreeMap<Address, u64>,
35    /// random seed bits of all slots in the cycle so far
36    pub rng_seed: BitVec<u8>,
37    /// Per-address production statistics
38    pub production_stats: PreHashMap<Address, ProductionStats>,
39    /// Snapshot of the final state hash
40    /// Used for PoS selections
41    pub final_state_hash_snapshot: Option<HashXof<HASH_XOF_SIZE_BYTES>>,
42}
43
44impl CycleInfo {
45    /// Create a new `CycleInfo`
46    pub fn new(
47        cycle: u64,
48        complete: bool,
49        roll_counts: BTreeMap<Address, u64>,
50        rng_seed: BitVec<u8>,
51        production_stats: PreHashMap<Address, ProductionStats>,
52    ) -> Self {
53        // create the new cycle
54        CycleInfo {
55            cycle,
56            complete,
57            roll_counts,
58            rng_seed,
59            production_stats,
60            final_state_hash_snapshot: None,
61        }
62    }
63}
64
65#[derive(Clone)]
66#[allow(missing_docs)]
67/// Serializer for `CycleInfo`
68pub struct CycleInfoSerializer {
69    pub u64_ser: U64VarIntSerializer,
70    pub bitvec_ser: BitVecSerializer,
71    pub production_stats_ser: ProductionStatsSerializer,
72    pub address_ser: AddressSerializer,
73    pub opt_hash_ser: OptionSerializer<HashXof<HASH_XOF_SIZE_BYTES>, HashXofSerializer>,
74}
75
76impl Default for CycleInfoSerializer {
77    fn default() -> Self {
78        Self::new()
79    }
80}
81
82impl CycleInfoSerializer {
83    /// Creates a new `CycleInfo` serializer
84    pub fn new() -> Self {
85        Self {
86            u64_ser: U64VarIntSerializer::new(),
87            bitvec_ser: BitVecSerializer::new(),
88            production_stats_ser: ProductionStatsSerializer::new(),
89            address_ser: AddressSerializer::new(),
90            opt_hash_ser: OptionSerializer::new(HashXofSerializer::new()),
91        }
92    }
93}
94
95impl Serializer<CycleInfo> for CycleInfoSerializer {
96    fn serialize(&self, value: &CycleInfo, buffer: &mut Vec<u8>) -> Result<(), SerializeError> {
97        // cycle_info.cycle
98        self.u64_ser.serialize(&value.cycle, buffer)?;
99
100        // cycle_info.complete
101        buffer.push(u8::from(value.complete));
102
103        // cycle_info.roll_counts
104        self.u64_ser
105            .serialize(&(value.roll_counts.len() as u64), buffer)?;
106        for (addr, count) in &value.roll_counts {
107            self.address_ser.serialize(addr, buffer)?;
108            self.u64_ser.serialize(count, buffer)?;
109        }
110
111        // cycle_info.rng_seed
112        self.bitvec_ser.serialize(&value.rng_seed, buffer)?;
113
114        // cycle_info.production_stats
115        self.production_stats_ser
116            .serialize(&value.production_stats, buffer)?;
117
118        // cycle_info.final_state_hash_snapshot
119        self.opt_hash_ser
120            .serialize(&value.final_state_hash_snapshot, buffer)?;
121
122        Ok(())
123    }
124}
125
126#[derive(Clone)]
127#[allow(missing_docs)]
128/// Deserializer for `CycleInfo`
129pub struct CycleInfoDeserializer {
130    pub u64_deser: U64VarIntDeserializer,
131    pub rolls_deser: RollsDeserializer,
132    pub bitvec_deser: BitVecDeserializer,
133    pub production_stats_deser: ProductionStatsDeserializer,
134    pub opt_hash_deser: OptionDeserializer<HashXof<HASH_XOF_SIZE_BYTES>, HashXofDeserializer>,
135}
136
137impl CycleInfoDeserializer {
138    /// Creates a new `CycleInfo` deserializer
139    pub fn new(max_rolls_length: u64, max_production_stats_length: u64) -> CycleInfoDeserializer {
140        CycleInfoDeserializer {
141            u64_deser: U64VarIntDeserializer::new(Included(u64::MIN), Included(u64::MAX)),
142            rolls_deser: RollsDeserializer::new(max_rolls_length),
143            bitvec_deser: BitVecDeserializer::new(),
144            production_stats_deser: ProductionStatsDeserializer::new(max_production_stats_length),
145            opt_hash_deser: OptionDeserializer::new(HashXofDeserializer::new()),
146        }
147    }
148}
149
150impl Deserializer<CycleInfo> for CycleInfoDeserializer {
151    fn deserialize<'a, E: ParseError<&'a [u8]> + ContextError<&'a [u8]>>(
152        &self,
153        buffer: &'a [u8],
154    ) -> IResult<&'a [u8], CycleInfo, E> {
155        context(
156            "cycle_history",
157            tuple((
158                context("cycle", |input| self.u64_deser.deserialize(input)),
159                context(
160                    "complete",
161                    alt((value(true, tag(&[1])), value(false, tag(&[0])))),
162                ),
163                context("roll_counts", |input| self.rolls_deser.deserialize(input)),
164                context("rng_seed", |input| self.bitvec_deser.deserialize(input)),
165                context("production_stats", |input| {
166                    self.production_stats_deser.deserialize(input)
167                }),
168                context("final_state_hash_snapshot", |input| {
169                    self.opt_hash_deser.deserialize(input)
170                }),
171            )),
172        )
173        .map(
174            #[allow(clippy::type_complexity)]
175            |(cycle, complete, roll_counts, rng_seed, production_stats, opt_hash): (
176                u64,                                  // cycle
177                bool,                                 // complete
178                Vec<(Address, u64)>,                  // roll_counts
179                BitVec<u8>,                           // rng_seed
180                PreHashMap<Address, ProductionStats>, // production_stats (address, n_success, n_fail)
181                Option<HashXof<HASH_XOF_SIZE_BYTES>>, // final_state_hash_snapshot
182            )| {
183                let mut cycle = CycleInfo::new(
184                    cycle,
185                    complete,
186                    roll_counts.into_iter().collect(),
187                    rng_seed,
188                    production_stats,
189                );
190                cycle.final_state_hash_snapshot = opt_hash;
191                cycle
192            },
193        )
194        .parse(buffer)
195    }
196}
197
198/// Block production statistics
199#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Deserialize, Serialize)]
200pub struct ProductionStats {
201    /// Number of successfully created blocks
202    pub block_success_count: u64,
203    /// Number of blocks missed
204    pub block_failure_count: u64,
205}
206
207impl ProductionStats {
208    /// Check if the production stats are above the required percentage
209    pub fn is_satisfying(&self, max_miss_ratio: &Ratio<u64>) -> bool {
210        let opportunities_count = self.block_success_count + self.block_failure_count;
211        if opportunities_count == 0 {
212            return true;
213        }
214        &Ratio::new(self.block_failure_count, opportunities_count) <= max_miss_ratio
215    }
216
217    /// Increment a production stat structure with another
218    pub fn extend(&mut self, stats: &ProductionStats) {
219        self.block_success_count = self
220            .block_success_count
221            .saturating_add(stats.block_success_count);
222        self.block_failure_count = self
223            .block_failure_count
224            .saturating_add(stats.block_failure_count);
225    }
226}
227
228#[derive(Clone)]
229#[allow(missing_docs)]
230/// Serializer for `ProductionStats`
231pub struct ProductionStatsSerializer {
232    pub u64_ser: U64VarIntSerializer,
233    address_ser: AddressSerializer,
234}
235
236impl Default for ProductionStatsSerializer {
237    fn default() -> Self {
238        Self::new()
239    }
240}
241
242impl ProductionStatsSerializer {
243    /// Creates a new `ProductionStats` serializer
244    pub fn new() -> Self {
245        Self {
246            u64_ser: U64VarIntSerializer::new(),
247            address_ser: AddressSerializer::new(),
248        }
249    }
250}
251
252impl Serializer<PreHashMap<Address, ProductionStats>> for ProductionStatsSerializer {
253    fn serialize(
254        &self,
255        value: &PreHashMap<Address, ProductionStats>,
256        buffer: &mut Vec<u8>,
257    ) -> Result<(), SerializeError> {
258        self.u64_ser.serialize(&(value.len() as u64), buffer)?;
259        for (
260            addr,
261            ProductionStats {
262                block_success_count,
263                block_failure_count,
264            },
265        ) in value.iter()
266        {
267            self.address_ser.serialize(addr, buffer)?;
268            self.u64_ser.serialize(block_success_count, buffer)?;
269            self.u64_ser.serialize(block_failure_count, buffer)?;
270        }
271        Ok(())
272    }
273}
274
275#[derive(Clone)]
276#[allow(missing_docs)]
277/// Deserializer for `ProductionStats`
278pub struct ProductionStatsDeserializer {
279    length_deserializer: U64VarIntDeserializer,
280    pub address_deserializer: AddressDeserializer,
281    pub u64_deserializer: U64VarIntDeserializer,
282}
283
284impl ProductionStatsDeserializer {
285    /// Creates a new `ProductionStats` deserializer
286    pub fn new(max_production_stats_length: u64) -> ProductionStatsDeserializer {
287        ProductionStatsDeserializer {
288            length_deserializer: U64VarIntDeserializer::new(
289                Included(u64::MIN),
290                Included(max_production_stats_length),
291            ),
292            address_deserializer: AddressDeserializer::new(),
293            u64_deserializer: U64VarIntDeserializer::new(Included(u64::MIN), Included(u64::MAX)),
294        }
295    }
296}
297
298impl Deserializer<PreHashMap<Address, ProductionStats>> for ProductionStatsDeserializer {
299    fn deserialize<'a, E: ParseError<&'a [u8]> + ContextError<&'a [u8]>>(
300        &self,
301        buffer: &'a [u8],
302    ) -> IResult<&'a [u8], PreHashMap<Address, ProductionStats>, E> {
303        context(
304            "Failed ProductionStats deserialization",
305            length_count(
306                context("Failed length deserialization", |input| {
307                    self.length_deserializer.deserialize(input)
308                }),
309                tuple((
310                    context("Failed address deserialization", |input| {
311                        self.address_deserializer.deserialize(input)
312                    }),
313                    context("Failed block_success_count deserialization", |input| {
314                        self.u64_deserializer.deserialize(input)
315                    }),
316                    context("Failed block_failure_count deserialization", |input| {
317                        self.u64_deserializer.deserialize(input)
318                    }),
319                )),
320            ),
321        )
322        .map(|elements| {
323            elements
324                .into_iter()
325                .map(|(addr, block_success_count, block_failure_count)| {
326                    (
327                        addr,
328                        ProductionStats {
329                            block_success_count,
330                            block_failure_count,
331                        },
332                    )
333                })
334                .collect()
335        })
336        .parse(buffer)
337    }
338}
339
340#[derive(Clone)]
341#[allow(missing_docs)]
342/// Deserializer for rolls
343pub struct RollsDeserializer {
344    length_deserializer: U64VarIntDeserializer,
345    pub address_deserializer: AddressDeserializer,
346    pub u64_deserializer: U64VarIntDeserializer,
347}
348
349impl RollsDeserializer {
350    /// Creates a new rolls deserializer
351    pub fn new(max_rolls_length: u64) -> RollsDeserializer {
352        RollsDeserializer {
353            length_deserializer: U64VarIntDeserializer::new(
354                Included(u64::MIN),
355                Included(max_rolls_length),
356            ),
357            address_deserializer: AddressDeserializer::new(),
358            u64_deserializer: U64VarIntDeserializer::new(Included(u64::MIN), Included(u64::MAX)),
359        }
360    }
361}
362
363impl Deserializer<Vec<(Address, u64)>> for RollsDeserializer {
364    fn deserialize<'a, E: ParseError<&'a [u8]> + ContextError<&'a [u8]>>(
365        &self,
366        buffer: &'a [u8],
367    ) -> IResult<&'a [u8], Vec<(Address, u64)>, E> {
368        context(
369            "Failed rolls deserialization",
370            length_count(
371                context("Failed length deserialization", |input| {
372                    self.length_deserializer.deserialize(input)
373                }),
374                tuple((
375                    context("Failed address deserialization", |input| {
376                        self.address_deserializer.deserialize(input)
377                    }),
378                    context("Failed number deserialization", |input| {
379                        self.u64_deserializer.deserialize(input)
380                    }),
381                )),
382            ),
383        )
384        .parse(buffer)
385    }
386}
387
388#[cfg(test)]
389mod test {
390    use super::*;
391    use massa_models::config::{MAX_PRODUCTION_STATS_LENGTH, MAX_ROLLS_COUNT_LENGTH};
392    use massa_serialization::DeserializeError;
393    use std::str::FromStr;
394
395    fn create_cycle_info() -> (CycleInfo, Address, Address) {
396        let addr1 =
397            Address::from_str("AU1jUbxeXW49QRT6Le5aPuNdcGWQV2kpnDyQkKoka4MmEUW3m8Xm").unwrap();
398        let addr2 =
399            Address::from_str("AU12nfJdBNotWffSEDDCS9mMXAxDbHbAVM9GW7pvVJoLxdCeeroX8").unwrap();
400        let mut prod_stats = PreHashMap::default();
401        prod_stats.insert(addr1, ProductionStats::default());
402        prod_stats.insert(
403            addr2,
404            ProductionStats {
405                block_success_count: 65539,
406                block_failure_count: 2,
407            },
408        );
409
410        let mut cycle_info1 = CycleInfo::new(
411            0,
412            false,
413            BTreeMap::from([(addr1, 1), (addr2, 100)]),
414            BitVec::new(),
415            prod_stats,
416        );
417        cycle_info1.final_state_hash_snapshot = Some(HashXof::from_bytes(&[2u8; 512]));
418
419        (cycle_info1, addr1, addr2)
420    }
421
422    #[test]
423    fn test_cycle_info_ser_der() {
424        let (cycle_info1, _, _) = create_cycle_info();
425
426        let mut buf = Vec::new();
427        let serializer = CycleInfoSerializer::new();
428        let deserializer =
429            CycleInfoDeserializer::new(MAX_ROLLS_COUNT_LENGTH, MAX_PRODUCTION_STATS_LENGTH);
430
431        serializer.serialize(&cycle_info1, &mut buf).unwrap();
432        let (rem, cycle_der) = deserializer.deserialize::<DeserializeError>(&buf).unwrap();
433        assert!(rem.is_empty());
434        assert_eq!(cycle_der, cycle_info1);
435
436        // With limits
437        let deserializer2 = CycleInfoDeserializer::new(1, MAX_PRODUCTION_STATS_LENGTH);
438        let deserializer3 = CycleInfoDeserializer::new(MAX_ROLLS_COUNT_LENGTH, 1);
439
440        buf.clear();
441        serializer.serialize(&cycle_info1, &mut buf).unwrap();
442        let res2 = deserializer2.deserialize::<DeserializeError>(&buf);
443        assert!(res2.is_err());
444        buf.clear();
445        serializer.serialize(&cycle_info1, &mut buf).unwrap();
446        let res3 = deserializer3.deserialize::<DeserializeError>(&buf);
447        assert!(res3.is_err());
448    }
449}