Skip to main content

mithril_common/entities/
signed_entity_config.rs

1use std::collections::BTreeSet;
2
3use serde::{Deserialize, Serialize};
4
5use crate::StdResult;
6use crate::entities::{
7    BlockNumber, BlockNumberOffset, BlockRange, CardanoDbBeacon, SignedEntityType,
8    SignedEntityTypeDiscriminants, TimePoint,
9};
10
11/// Convert [TimePoint] to [SignedEntityType] and list allowed signed entity types and
12/// discriminants.
13#[derive(Clone, Debug, PartialEq, Eq)]
14pub struct SignedEntityConfig {
15    /// List of discriminants that the node is allowed to sign
16    pub allowed_discriminants: BTreeSet<SignedEntityTypeDiscriminants>,
17    /// Cardano transactions signing configuration
18    pub cardano_transactions_signing_config: Option<CardanoTransactionsSigningConfig>,
19    /// Cardano blocks and transactions signing configuration
20    pub cardano_blocks_transactions_signing_config: Option<CardanoBlocksTransactionsSigningConfig>,
21}
22
23impl SignedEntityConfig {
24    /// Default allowed discriminants
25    ///
26    /// Appended to the allowed discriminants in the configuration.
27    pub const DEFAULT_ALLOWED_DISCRIMINANTS: [SignedEntityTypeDiscriminants; 1] =
28        [SignedEntityTypeDiscriminants::MithrilStakeDistribution];
29
30    /// Append to the given list of allowed signed entity types discriminants the [Self::DEFAULT_ALLOWED_DISCRIMINANTS]
31    /// if not already present.
32    pub fn append_allowed_signed_entity_types_discriminants(
33        discriminants: BTreeSet<SignedEntityTypeDiscriminants>,
34    ) -> BTreeSet<SignedEntityTypeDiscriminants> {
35        let mut discriminants = discriminants;
36        discriminants.append(&mut BTreeSet::from(Self::DEFAULT_ALLOWED_DISCRIMINANTS));
37        discriminants
38    }
39
40    /// Create the deduplicated list of allowed signed entity types discriminants.
41    ///
42    /// The list is the aggregation of [Self::DEFAULT_ALLOWED_DISCRIMINANTS] and
43    /// `allowed_discriminants`.
44    pub fn list_allowed_signed_entity_types_discriminants(
45        &self,
46    ) -> BTreeSet<SignedEntityTypeDiscriminants> {
47        let discriminants = self.allowed_discriminants.clone();
48        Self::append_allowed_signed_entity_types_discriminants(discriminants)
49    }
50
51    /// Convert this time point to a signed entity type based on the given discriminant.
52    pub fn time_point_to_signed_entity<D: Into<SignedEntityTypeDiscriminants>>(
53        &self,
54        discriminant: D,
55        time_point: &TimePoint,
56    ) -> StdResult<SignedEntityType> {
57        let signed_entity_type = match discriminant.into() {
58            SignedEntityTypeDiscriminants::MithrilStakeDistribution => {
59                SignedEntityType::MithrilStakeDistribution(time_point.epoch)
60            }
61            SignedEntityTypeDiscriminants::CardanoStakeDistribution => {
62                SignedEntityType::CardanoStakeDistribution(time_point.epoch.previous()?)
63            }
64            SignedEntityTypeDiscriminants::CardanoImmutableFilesFull => {
65                SignedEntityType::CardanoImmutableFilesFull(CardanoDbBeacon::new(
66                    *time_point.epoch,
67                    time_point.immutable_file_number,
68                ))
69            }
70            SignedEntityTypeDiscriminants::CardanoTransactions => {
71                match &self.cardano_transactions_signing_config {
72                    Some(config) => SignedEntityType::CardanoTransactions(
73                        time_point.epoch,
74                        config
75                            .compute_block_number_to_be_signed(time_point.chain_point.block_number),
76                    ),
77                    None => {
78                        anyhow::bail!(
79                            "Can't derive a `CardanoTransactions` signed entity type from a time point without a `CardanoTransactionsSigningConfig`"
80                        )
81                    }
82                }
83            }
84            SignedEntityTypeDiscriminants::CardanoBlocksTransactions => {
85                match &self.cardano_blocks_transactions_signing_config {
86                    Some(config) => SignedEntityType::CardanoBlocksTransactions(
87                        time_point.epoch,
88                        config
89                            .compute_block_number_to_be_signed(time_point.chain_point.block_number),
90                        config.security_parameter,
91                    ),
92                    None => {
93                        anyhow::bail!(
94                            "Can't derive a `CardanoBlocksTransactions` signed entity type from a time point without a `CardanoBlocksTransactionsSigningConfig`"
95                        )
96                    }
97                }
98            }
99            SignedEntityTypeDiscriminants::CardanoDatabase => SignedEntityType::CardanoDatabase(
100                CardanoDbBeacon::new(*time_point.epoch, time_point.immutable_file_number),
101            ),
102        };
103
104        Ok(signed_entity_type)
105    }
106
107    /// Create the deduplicated list of allowed signed entity types discriminants.
108    ///
109    /// The list is the aggregation of [Self::DEFAULT_ALLOWED_DISCRIMINANTS] and
110    /// `allowed_discriminants`.
111    pub fn list_allowed_signed_entity_types(
112        &self,
113        time_point: &TimePoint,
114    ) -> StdResult<Vec<SignedEntityType>> {
115        self.list_allowed_signed_entity_types_discriminants()
116            .into_iter()
117            .map(|discriminant| self.time_point_to_signed_entity(discriminant, time_point))
118            .collect()
119    }
120}
121
122/// Configuration for the signing of Cardano transactions
123///
124/// Allow to compute the block number to be signed based on the chain tip block number.
125///
126#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
127pub struct CardanoTransactionsSigningConfig {
128    /// Number of blocks to discard from the tip of the chain when importing transactions.
129    pub security_parameter: BlockNumberOffset,
130
131    /// The number of blocks between signature of the transactions.
132    ///
133    /// *Note: The step is adjusted to be a multiple of the block range length in order
134    /// to guarantee that the block number signed in a certificate is effectively signed.*
135    pub step: BlockNumber,
136}
137
138impl CardanoTransactionsSigningConfig {
139    /// Compute the block number to be signed based on the chain tip block number.
140    ///
141    /// The latest block number to be signed is the highest multiple of the step less or equal than the
142    /// block number minus the security parameter.
143    ///
144    /// The formula is as follows:
145    ///
146    /// `block_number = ⌊(tip.block_number - security_parameter) / step⌋ × step - 1`
147    ///
148    /// where `⌊x⌋` is the floor function that rounds to the greatest integer less than or equal to `x`.
149    ///
150    /// *Notes:*
151    /// * *The step is adjusted to be a multiple of the block range length in order
152    ///   to guarantee that the block number signed in a certificate is effectively signed.*
153    /// * *1 is subtracted to the result because the block range end is exclusive (ie: a BlockRange over
154    ///   `30..45` finishes at 44 included, 45 is included in the next block range).*
155    pub fn compute_block_number_to_be_signed(&self, block_number: BlockNumber) -> BlockNumber {
156        let adjusted_step = BlockRange::from_block_number(self.step).start;
157        // We can't have a step lower than the block range length.
158        let adjusted_step = std::cmp::max(adjusted_step, BlockRange::LENGTH);
159
160        let block_number_to_be_signed =
161            compute_block_number_to_be_signed(block_number, self.security_parameter, adjusted_step);
162        block_number_to_be_signed - 1
163    }
164}
165
166/// Configuration for the signing of Cardano blocks and transactions
167#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
168pub struct CardanoBlocksTransactionsSigningConfig {
169    /// Number of blocks to discard from the tip of the chain when importing blocks and transactions.
170    pub security_parameter: BlockNumberOffset,
171
172    /// The number of blocks between signature of the blocks and transactions.
173    ///
174    /// *Note: The step is adjusted to be a multiple of the block range length in order
175    /// to guarantee that the block number signed in a certificate is effectively signed.*
176    pub step: BlockNumber,
177}
178
179impl CardanoBlocksTransactionsSigningConfig {
180    /// Compute the block number to be signed based on the chain tip block number.
181    ///
182    /// The latest block number to be signed is the highest multiple of the step less or equal than the
183    /// block number minus the security parameter.
184    ///
185    /// The formula is as follows:
186    ///
187    /// `block_number = ⌊(tip.block_number - security_parameter) / step⌋ × step`
188    ///
189    /// where `⌊x⌋` is the floor function that rounds to the greatest integer less than or equal to `x`.
190    pub fn compute_block_number_to_be_signed(&self, block_number: BlockNumber) -> BlockNumber {
191        compute_block_number_to_be_signed(block_number, self.security_parameter, self.step)
192    }
193}
194
195/// Compute the block number to be signed based on the chain tip block number.
196fn compute_block_number_to_be_signed(
197    block_number: BlockNumber,
198    security_parameter: BlockNumberOffset,
199    step: BlockNumber,
200) -> BlockNumber {
201    let adjusted_step = std::cmp::max(step, BlockNumber(1));
202    (block_number - security_parameter) / adjusted_step * adjusted_step
203}
204
205#[cfg(test)]
206mod tests {
207    use crate::entities::{
208        BlockNumberOffset, CardanoDbBeacon, ChainPoint, Epoch, SignedEntityType, SlotNumber,
209        TimePoint,
210    };
211    use crate::test::{double::Dummy, double::fake_data};
212
213    use super::*;
214
215    #[test]
216    fn given_discriminant_convert_to_signed_entity() {
217        let time_point = TimePoint {
218            epoch: Epoch(1),
219            immutable_file_number: 5,
220            chain_point: ChainPoint {
221                slot_number: SlotNumber(73),
222                block_number: BlockNumber(20),
223                block_hash: "block_hash-20".to_string(),
224            },
225        };
226        let config = SignedEntityConfig {
227            allowed_discriminants: SignedEntityTypeDiscriminants::all(),
228            cardano_transactions_signing_config: Some(CardanoTransactionsSigningConfig {
229                security_parameter: BlockNumberOffset(0),
230                step: BlockNumber(15),
231            }),
232            cardano_blocks_transactions_signing_config: Some(
233                CardanoBlocksTransactionsSigningConfig {
234                    security_parameter: BlockNumberOffset(5),
235                    step: BlockNumber(15),
236                },
237            ),
238        };
239
240        assert_eq!(
241            SignedEntityType::MithrilStakeDistribution(Epoch(1)),
242            config
243                .time_point_to_signed_entity(
244                    SignedEntityTypeDiscriminants::MithrilStakeDistribution,
245                    &time_point
246                )
247                .unwrap()
248        );
249
250        // An offset of -1 is applied to the epoch of the time point to get the epoch of the stake distribution to be signed
251        assert_eq!(
252            SignedEntityType::CardanoStakeDistribution(Epoch(0)),
253            config
254                .time_point_to_signed_entity(
255                    SignedEntityTypeDiscriminants::CardanoStakeDistribution,
256                    &time_point
257                )
258                .unwrap()
259        );
260
261        assert_eq!(
262            SignedEntityType::CardanoImmutableFilesFull(CardanoDbBeacon::new(1, 5)),
263            config
264                .time_point_to_signed_entity(
265                    SignedEntityTypeDiscriminants::CardanoImmutableFilesFull,
266                    &time_point
267                )
268                .unwrap()
269        );
270
271        // The block number to be signed is 14 because the step is 15, the block number is 20, and
272        // the security parameter is 0.
273        // This is further tested in the "compute_block_number_to_be_signed_for_cardano_transactions" tests below.
274        assert_eq!(
275            SignedEntityType::CardanoTransactions(Epoch(1), BlockNumber(14)),
276            config
277                .time_point_to_signed_entity(
278                    SignedEntityTypeDiscriminants::CardanoTransactions,
279                    &time_point
280                )
281                .unwrap()
282        );
283
284        // The block number to be signed is 15 because the step is 15, the block number is 20, and
285        // the security parameter is 5.
286        // This is further tested in the "compute_block_number_to_be_signed_for_cardano_blocks_transactions" tests below.
287        assert_eq!(
288            SignedEntityType::CardanoBlocksTransactions(
289                Epoch(1),
290                BlockNumber(15),
291                BlockNumberOffset(5)
292            ),
293            config
294                .time_point_to_signed_entity(
295                    SignedEntityTypeDiscriminants::CardanoBlocksTransactions,
296                    &time_point
297                )
298                .unwrap()
299        );
300
301        assert_eq!(
302            SignedEntityType::CardanoDatabase(CardanoDbBeacon::new(1, 5)),
303            config
304                .time_point_to_signed_entity(
305                    SignedEntityTypeDiscriminants::CardanoDatabase,
306                    &time_point
307                )
308                .unwrap()
309        );
310    }
311
312    #[test]
313    fn can_not_convert_time_point_to_cardano_transaction_without_the_associated_config() {
314        let time_point = TimePoint {
315            epoch: Epoch(1),
316            immutable_file_number: 5,
317            chain_point: ChainPoint {
318                slot_number: SlotNumber(73),
319                block_number: BlockNumber(20),
320                block_hash: "block_hash-20".to_string(),
321            },
322        };
323        let config = SignedEntityConfig {
324            allowed_discriminants: SignedEntityTypeDiscriminants::all(),
325            cardano_transactions_signing_config: None,
326            cardano_blocks_transactions_signing_config: None,
327        };
328
329        let error = config
330            .time_point_to_signed_entity(
331                SignedEntityTypeDiscriminants::CardanoTransactions,
332                &time_point,
333            )
334            .unwrap_err();
335
336        let expected_error = "Can't derive a `CardanoTransactions` signed entity type from a time point without a `CardanoTransactionsSigningConfig`";
337        assert!(
338            error.to_string().contains(expected_error),
339            "Error message: {error:?}\nshould contains: {expected_error}\n"
340        );
341    }
342
343    mod compute_block_number_to_be_signed_for_cardano_transactions {
344        use super::*;
345
346        #[test]
347        fn compute_without_a_security_parameter() {
348            // **block_number = ((tip.block_number - k') / n) × n**
349            let block_number = BlockNumber(105);
350            let signing_config = CardanoTransactionsSigningConfig {
351                security_parameter: BlockNumberOffset(0),
352                step: BlockNumber(15),
353            };
354            assert_eq!(
355                signing_config.compute_block_number_to_be_signed(block_number),
356                104
357            );
358        }
359
360        #[test]
361        fn compute_with_security_parameter_lower_than_block_number() {
362            let block_number = BlockNumber(100);
363            let signing_config = CardanoTransactionsSigningConfig {
364                security_parameter: BlockNumberOffset(5),
365                step: BlockNumber(15),
366            };
367            assert_eq!(
368                signing_config.compute_block_number_to_be_signed(block_number),
369                89
370            );
371        }
372
373        #[test]
374        fn when_security_parameter_plus_step_equal_to_block_number_return_step_minus_1() {
375            let block_number = BlockNumber(100);
376            let signing_config = CardanoTransactionsSigningConfig {
377                security_parameter: BlockNumberOffset(85),
378                step: BlockNumber(15),
379            };
380            assert_eq!(
381                signing_config.compute_block_number_to_be_signed(block_number),
382                14
383            );
384        }
385
386        #[test]
387        fn when_step_higher_than_block_number_return_0() {
388            let block_number = BlockNumber(29);
389            let signing_config = CardanoTransactionsSigningConfig {
390                security_parameter: BlockNumberOffset(0),
391                step: BlockNumber(30),
392            };
393            assert_eq!(
394                signing_config.compute_block_number_to_be_signed(block_number),
395                0
396            );
397        }
398
399        #[test]
400        fn should_not_overlow_on_security_parameter() {
401            let block_number = BlockNumber(50);
402            let signing_config = CardanoTransactionsSigningConfig {
403                security_parameter: BlockNumberOffset(100),
404                step: BlockNumber(30),
405            };
406            assert_eq!(
407                signing_config.compute_block_number_to_be_signed(block_number),
408                0
409            );
410        }
411
412        #[test]
413        fn round_step_to_previous_block_range_start_when_step_right_below_said_block_range_start() {
414            let block_number = BlockRange::LENGTH * 5 + 1;
415            let signing_config = CardanoTransactionsSigningConfig {
416                security_parameter: BlockNumberOffset(0),
417                step: BlockRange::LENGTH * 2 - 1,
418            };
419            assert_eq!(
420                signing_config.compute_block_number_to_be_signed(block_number),
421                BlockRange::LENGTH * 5 - 1
422            );
423        }
424
425        #[test]
426        fn round_step_to_block_range_start_when_step_right_after_said_block_range_start() {
427            let block_number = BlockRange::LENGTH * 5 + 1;
428            let signing_config = CardanoTransactionsSigningConfig {
429                security_parameter: BlockNumberOffset(0),
430                step: BlockRange::LENGTH * 2 + 1,
431            };
432            assert_eq!(
433                signing_config.compute_block_number_to_be_signed(block_number),
434                BlockRange::LENGTH * 4 - 1
435            );
436        }
437
438        #[test]
439        fn step_lower_than_block_range_length_is_adjusted_to_block_range_length() {
440            // Adjusted step is always at least BLOCK_RANGE_LENGTH.
441            let block_number = BlockRange::LENGTH * 10 - 1;
442            let signing_config = CardanoTransactionsSigningConfig {
443                security_parameter: BlockNumberOffset(0),
444                step: BlockRange::LENGTH - 1,
445            };
446            assert_eq!(
447                signing_config.compute_block_number_to_be_signed(block_number),
448                BlockRange::LENGTH * 9 - 1
449            );
450        }
451
452        #[test]
453        fn step_and_block_number_below_block_range_length_returns_0() {
454            // Adjusted step is always at least BLOCK_RANGE_LENGTH.
455            let block_number = BlockRange::LENGTH - 1;
456            let signing_config = CardanoTransactionsSigningConfig {
457                security_parameter: BlockNumberOffset(0),
458                step: BlockRange::LENGTH - 1,
459            };
460            assert_eq!(
461                signing_config.compute_block_number_to_be_signed(block_number),
462                0
463            );
464        }
465    }
466
467    mod compute_block_number_to_be_signed_for_cardano_blocks_transactions {
468        use super::*;
469
470        #[test]
471        fn compute_without_a_security_parameter() {
472            // **block_number = ((tip.block_number - k') / n) × n**
473            let block_number = BlockNumber(105);
474            let signing_config = CardanoBlocksTransactionsSigningConfig {
475                security_parameter: BlockNumberOffset(0),
476                step: BlockNumber(15),
477            };
478            // ((105 - 0).div_euclid(15) * 15)  = (105.div_euclid(15) * 15)  = 7 * 15  = 105
479            assert_eq!(
480                signing_config.compute_block_number_to_be_signed(block_number),
481                105
482            );
483        }
484
485        #[test]
486        fn compute_with_security_parameter_lower_than_block_number() {
487            let block_number = BlockNumber(100);
488            let signing_config = CardanoBlocksTransactionsSigningConfig {
489                security_parameter: BlockNumberOffset(5),
490                step: BlockNumber(15),
491            };
492            // ((100 - 5).div_euclid(15) * 15)  = (95.div_euclid(15) * 15)  = 6 * 15  = 90
493            assert_eq!(
494                signing_config.compute_block_number_to_be_signed(block_number),
495                90
496            );
497        }
498
499        #[test]
500        fn when_security_parameter_plus_step_equal_to_block_number_return_step_minus_1() {
501            let block_number = BlockNumber(100);
502            let signing_config = CardanoBlocksTransactionsSigningConfig {
503                security_parameter: BlockNumberOffset(85),
504                step: BlockNumber(15),
505            };
506            // ((100 - 85).div_euclid(15) * 15)  = (15.div_euclid(15) * 15)  = 1 * 15  = 15
507            assert_eq!(
508                signing_config.compute_block_number_to_be_signed(block_number),
509                15
510            );
511        }
512
513        #[test]
514        fn when_step_higher_than_block_number_return_0() {
515            let block_number = BlockNumber(29);
516            let signing_config = CardanoBlocksTransactionsSigningConfig {
517                security_parameter: BlockNumberOffset(0),
518                step: BlockNumber(30),
519            };
520            assert_eq!(
521                signing_config.compute_block_number_to_be_signed(block_number),
522                0
523            );
524        }
525
526        #[test]
527        fn should_not_overlow_on_security_parameter() {
528            let block_number = BlockNumber(50);
529            let signing_config = CardanoBlocksTransactionsSigningConfig {
530                security_parameter: BlockNumberOffset(100),
531                step: BlockNumber(30),
532            };
533            assert_eq!(
534                signing_config.compute_block_number_to_be_signed(block_number),
535                0
536            );
537        }
538
539        #[test]
540        fn can_use_step_right_below_than_a_multiple_of_a_block_range_length() {
541            let block_number = BlockNumber(150);
542            let signing_config = CardanoBlocksTransactionsSigningConfig {
543                security_parameter: BlockNumberOffset(0),
544                step: BlockRange::LENGTH - 1,
545            };
546            // ((150 - 0).div_euclid(15 - 1) * (15 - 1)) = (150.div_euclid(14) * 14) - 1 = 10 * 14 = 140
547            assert_eq!(
548                signing_config.compute_block_number_to_be_signed(block_number),
549                140,
550            );
551        }
552
553        #[test]
554        fn can_use_step_right_after_a_multiple_of_a_block_range_length() {
555            let block_number = BlockNumber(150);
556            let signing_config = CardanoBlocksTransactionsSigningConfig {
557                security_parameter: BlockNumberOffset(0),
558                step: BlockRange::LENGTH + 1,
559            };
560            // ((150 - 0).div_euclid(15 + 1) * (15 + 1))  = (150.div_euclid(16) * 16)  = 9 * 16  = 144
561            assert_eq!(
562                signing_config.compute_block_number_to_be_signed(block_number),
563                144,
564            );
565        }
566
567        #[test]
568        fn can_use_step_higher_than_a_block_range_length_and_out_not_equal_to_a_range_boundaries() {
569            let block_number = BlockNumber(150);
570            let signing_config = CardanoBlocksTransactionsSigningConfig {
571                security_parameter: BlockNumberOffset(0),
572                step: BlockRange::LENGTH + 6,
573            };
574            // ((150 - 0).div_euclid(15 + 6) * (15 + 6))  = (150.div_euclid(21) * 21)  = 7 * 21  = 147
575            assert_eq!(
576                signing_config.compute_block_number_to_be_signed(block_number),
577                147,
578            );
579        }
580
581        #[test]
582        fn can_use_step_lower_than_a_block_range_length_and_out_not_equal_to_a_range_boundaries() {
583            let block_number = BlockNumber(150);
584            let signing_config = CardanoBlocksTransactionsSigningConfig {
585                security_parameter: BlockNumberOffset(0),
586                step: BlockRange::LENGTH - 6,
587            };
588            // ((150 - 0).div_euclid(15 - 6) * (15 - 6))  = (150.div_euclid(9) * 9)  = 16 * 9  = 144
589            assert_eq!(
590                signing_config.compute_block_number_to_be_signed(block_number),
591                144,
592            );
593        }
594
595        #[test]
596        fn can_use_a_step_and_block_number_both_below_block_range_length() {
597            let block_number = BlockRange::LENGTH - 1;
598            let signing_config = CardanoBlocksTransactionsSigningConfig {
599                security_parameter: BlockNumberOffset(0),
600                step: BlockRange::LENGTH - 10,
601            };
602            // ((15 - 1).div_euclid(15 - 10) * (15 - 10))  = (14.div_euclid(5) * 5)  = 2 * 5  = 10
603            assert_eq!(
604                signing_config.compute_block_number_to_be_signed(block_number),
605                10,
606            );
607        }
608    }
609
610    #[test]
611    fn test_list_allowed_signed_entity_types_discriminant_without_specific_configuration() {
612        let config = SignedEntityConfig {
613            allowed_discriminants: BTreeSet::new(),
614            ..SignedEntityConfig::dummy()
615        };
616
617        let discriminants = config.list_allowed_signed_entity_types_discriminants();
618
619        assert_eq!(
620            BTreeSet::from(SignedEntityConfig::DEFAULT_ALLOWED_DISCRIMINANTS),
621            discriminants
622        );
623    }
624
625    #[test]
626    fn test_list_allowed_signed_entity_types_discriminant_should_not_duplicate_a_signed_entity_discriminant_type_already_in_default_ones()
627     {
628        let config = SignedEntityConfig {
629            allowed_discriminants: BTreeSet::from([
630                SignedEntityConfig::DEFAULT_ALLOWED_DISCRIMINANTS[0],
631            ]),
632            ..SignedEntityConfig::dummy()
633        };
634
635        let discriminants = config.list_allowed_signed_entity_types_discriminants();
636
637        assert_eq!(
638            BTreeSet::from(SignedEntityConfig::DEFAULT_ALLOWED_DISCRIMINANTS),
639            discriminants
640        );
641    }
642
643    #[test]
644    fn test_list_allowed_signed_entity_types_discriminants_should_add_configured_discriminants() {
645        let config = SignedEntityConfig {
646            allowed_discriminants: BTreeSet::from([
647                SignedEntityTypeDiscriminants::CardanoStakeDistribution,
648                SignedEntityTypeDiscriminants::CardanoTransactions,
649                SignedEntityTypeDiscriminants::CardanoDatabase,
650            ]),
651            ..SignedEntityConfig::dummy()
652        };
653
654        let discriminants = config.list_allowed_signed_entity_types_discriminants();
655
656        assert_eq!(
657            BTreeSet::from_iter(
658                [
659                    SignedEntityConfig::DEFAULT_ALLOWED_DISCRIMINANTS.as_slice(),
660                    [
661                        SignedEntityTypeDiscriminants::CardanoStakeDistribution,
662                        SignedEntityTypeDiscriminants::CardanoTransactions,
663                        SignedEntityTypeDiscriminants::CardanoDatabase
664                    ]
665                    .as_slice()
666                ]
667                .concat()
668            ),
669            discriminants
670        );
671    }
672
673    #[test]
674    fn test_list_allowed_signed_entity_types_discriminants_with_multiple_identical_signed_entity_types_in_configuration_should_not_be_added_several_times()
675     {
676        let config = SignedEntityConfig {
677            allowed_discriminants: BTreeSet::from([
678                SignedEntityTypeDiscriminants::CardanoTransactions,
679                SignedEntityTypeDiscriminants::CardanoTransactions,
680                SignedEntityTypeDiscriminants::CardanoTransactions,
681            ]),
682            ..SignedEntityConfig::dummy()
683        };
684
685        let discriminants = config.list_allowed_signed_entity_types_discriminants();
686
687        assert_eq!(
688            BTreeSet::from_iter(
689                [
690                    SignedEntityConfig::DEFAULT_ALLOWED_DISCRIMINANTS.as_slice(),
691                    [SignedEntityTypeDiscriminants::CardanoTransactions].as_slice()
692                ]
693                .concat()
694            ),
695            discriminants
696        );
697    }
698
699    #[test]
700    fn test_list_allowed_signed_entity_types_with_specific_configuration() {
701        let beacon = fake_data::beacon();
702        let chain_point = ChainPoint {
703            block_number: BlockNumber(45),
704            ..ChainPoint::dummy()
705        };
706        let time_point = TimePoint::new(
707            *beacon.epoch,
708            beacon.immutable_file_number,
709            chain_point.clone(),
710        );
711        let config = SignedEntityConfig {
712            allowed_discriminants: BTreeSet::from([
713                SignedEntityTypeDiscriminants::CardanoStakeDistribution,
714                SignedEntityTypeDiscriminants::CardanoTransactions,
715                SignedEntityTypeDiscriminants::CardanoBlocksTransactions,
716            ]),
717            cardano_transactions_signing_config: Some(CardanoTransactionsSigningConfig {
718                security_parameter: BlockNumberOffset(0),
719                step: BlockNumber(15),
720            }),
721            cardano_blocks_transactions_signing_config: Some(
722                CardanoBlocksTransactionsSigningConfig {
723                    security_parameter: BlockNumberOffset(0),
724                    step: BlockNumber(15),
725                },
726            ),
727        };
728
729        let signed_entity_types = config.list_allowed_signed_entity_types(&time_point).unwrap();
730
731        assert_eq!(
732            vec![
733                SignedEntityType::MithrilStakeDistribution(beacon.epoch),
734                SignedEntityType::CardanoStakeDistribution(beacon.epoch - 1),
735                SignedEntityType::CardanoTransactions(beacon.epoch, chain_point.block_number - 1),
736                SignedEntityType::CardanoBlocksTransactions(
737                    beacon.epoch,
738                    chain_point.block_number,
739                    BlockNumberOffset(0)
740                ),
741            ],
742            signed_entity_types
743        );
744    }
745}