mithril_client_cli/commands/tools/
aggregator_discovery.rs

1use clap::Parser;
2use cli_table::{Cell, Table, print_stdout};
3
4use mithril_client::{
5    AggregatorDiscoveryType, ClientBuilder, MithrilResult, RequiredAggregatorCapabilities,
6    common::{AggregateSignatureType, MithrilNetwork, SignedEntityTypeDiscriminants},
7};
8
9use crate::CommandContext;
10
11/// Clap command to select an aggregator from the available ones with automatic discovery.
12#[derive(Parser, Debug, Clone)]
13pub struct AggregatorDiscoveryCommand {
14    /// Mithril network name
15    network: MithrilNetwork,
16
17    /// Maximum number of entries to retrieve
18    #[clap(long, default_value_t = 1)]
19    max_entries: usize,
20
21    /// Signed entity types to consider for the discovery
22    ///
23    /// If not provided, all signed entity types are considered.
24    #[clap(long, value_parser, num_args = 0.., value_delimiter = ',')]
25    signed_entity_types: Vec<SignedEntityTypeDiscriminants>,
26
27    /// Aggregate signature types to consider for the discovery
28    ///
29    /// If not provided, all aggregate signature types are considered.
30    #[clap(long, value_parser, num_args = 0.., value_delimiter = ',')]
31    aggregate_signature_types: Vec<AggregateSignatureType>,
32}
33
34impl AggregatorDiscoveryCommand {
35    /// Main command execution
36    pub async fn execute(&self, context: CommandContext) -> MithrilResult<()> {
37        let required_capabilities = self.build_required_capabilities();
38        let client_builder =
39            ClientBuilder::new(AggregatorDiscoveryType::Automatic(self.network.clone()))
40                .with_capabilities(required_capabilities);
41        let aggregator_endpoints = client_builder
42            .discover_aggregator(&self.network)?
43            .take(self.max_entries);
44        let lines = aggregator_endpoints.collect::<Vec<_>>();
45
46        if context.is_json_output_enabled() {
47            println!("{}", serde_json::to_string(&lines)?);
48        } else {
49            let lines = lines
50                .into_iter()
51                .map(|endpoint| {
52                    let endpoint_clone = endpoint.clone();
53                    let capabilities = endpoint_clone.capabilities();
54                    vec![
55                        endpoint.cell(),
56                        capabilities.aggregate_signature_type.cell(),
57                        capabilities
58                            .signed_entity_types
59                            .iter()
60                            .map(|signed_entity_type| signed_entity_type.to_string())
61                            .collect::<Vec<_>>()
62                            .join(",")
63                            .cell(),
64                    ]
65                })
66                .collect::<Vec<_>>()
67                .table()
68                .title(vec![
69                    "Aggregator Endpoint".cell(),
70                    "Aggregate Signature Type".cell(),
71                    "Signed Entity Types".cell(),
72                ]);
73            print_stdout(lines)?;
74        }
75
76        Ok(())
77    }
78
79    fn build_required_capabilities(&self) -> RequiredAggregatorCapabilities {
80        if self.signed_entity_types.is_empty() && self.aggregate_signature_types.is_empty() {
81            return RequiredAggregatorCapabilities::All;
82        }
83
84        let mut required_capabilities = vec![];
85        if !self.signed_entity_types.is_empty() {
86            let mut required_capabilities_signed_entity_types = vec![];
87            for signed_entity_type in &self.signed_entity_types {
88                required_capabilities_signed_entity_types.push(
89                    RequiredAggregatorCapabilities::SignedEntityType(*signed_entity_type),
90                );
91            }
92            required_capabilities.push(RequiredAggregatorCapabilities::And(
93                required_capabilities_signed_entity_types,
94            ));
95        }
96
97        if !self.aggregate_signature_types.is_empty() {
98            let mut required_capabilities_aggregate_signature_types = vec![];
99            for aggregate_signature_type in &self.aggregate_signature_types {
100                required_capabilities_aggregate_signature_types.push(
101                    RequiredAggregatorCapabilities::AggregateSignatureType(
102                        *aggregate_signature_type,
103                    ),
104                );
105            }
106            required_capabilities.push(RequiredAggregatorCapabilities::And(
107                required_capabilities_aggregate_signature_types,
108            ));
109        }
110        if required_capabilities.len() == 1 {
111            required_capabilities.into_iter().next().unwrap()
112        } else {
113            RequiredAggregatorCapabilities::And(required_capabilities)
114        }
115    }
116}
117
118#[cfg(test)]
119mod tests {
120    use super::*;
121    use mithril_client::common::SignedEntityTypeDiscriminants;
122
123    #[test]
124    fn test_build_required_capabilities_all() {
125        let command = AggregatorDiscoveryCommand {
126            network: MithrilNetwork::dummy(),
127            max_entries: 1,
128            signed_entity_types: vec![],
129            aggregate_signature_types: vec![],
130        };
131
132        let required_capabilities = command.build_required_capabilities();
133        assert_eq!(required_capabilities, RequiredAggregatorCapabilities::All);
134    }
135
136    #[test]
137    fn test_build_required_capabilities_signed_entity_types() {
138        let command = AggregatorDiscoveryCommand {
139            network: MithrilNetwork::dummy(),
140            max_entries: 1,
141            signed_entity_types: vec![
142                SignedEntityTypeDiscriminants::CardanoTransactions,
143                SignedEntityTypeDiscriminants::CardanoStakeDistribution,
144            ],
145            aggregate_signature_types: vec![],
146        };
147
148        let required_capabilities = command.build_required_capabilities();
149
150        assert_eq!(
151            required_capabilities,
152            RequiredAggregatorCapabilities::And(vec![
153                RequiredAggregatorCapabilities::SignedEntityType(
154                    SignedEntityTypeDiscriminants::CardanoTransactions
155                ),
156                RequiredAggregatorCapabilities::SignedEntityType(
157                    SignedEntityTypeDiscriminants::CardanoStakeDistribution
158                ),
159            ])
160        );
161    }
162
163    #[test]
164    fn test_build_required_capabilities_aggregate_signature_types() {
165        let command = AggregatorDiscoveryCommand {
166            network: MithrilNetwork::dummy(),
167            max_entries: 1,
168            signed_entity_types: vec![],
169            aggregate_signature_types: vec![AggregateSignatureType::Concatenation],
170        };
171        let required_capabilities = command.build_required_capabilities();
172
173        assert_eq!(
174            required_capabilities,
175            RequiredAggregatorCapabilities::And(vec![
176                RequiredAggregatorCapabilities::AggregateSignatureType(
177                    AggregateSignatureType::Concatenation
178                ),
179            ])
180        );
181    }
182
183    #[test]
184    fn test_build_required_capabilities_both() {
185        let command = AggregatorDiscoveryCommand {
186            network: MithrilNetwork::dummy(),
187            max_entries: 1,
188            signed_entity_types: vec![
189                SignedEntityTypeDiscriminants::CardanoTransactions,
190                SignedEntityTypeDiscriminants::CardanoStakeDistribution,
191            ],
192            aggregate_signature_types: vec![AggregateSignatureType::Concatenation],
193        };
194        let required_capabilities = command.build_required_capabilities();
195
196        assert_eq!(
197            required_capabilities,
198            RequiredAggregatorCapabilities::And(vec![
199                RequiredAggregatorCapabilities::And(vec![
200                    RequiredAggregatorCapabilities::SignedEntityType(
201                        SignedEntityTypeDiscriminants::CardanoTransactions
202                    ),
203                    RequiredAggregatorCapabilities::SignedEntityType(
204                        SignedEntityTypeDiscriminants::CardanoStakeDistribution
205                    ),
206                ]),
207                RequiredAggregatorCapabilities::And(vec![
208                    RequiredAggregatorCapabilities::AggregateSignatureType(
209                        AggregateSignatureType::Concatenation
210                    ),
211                ]),
212            ])
213        );
214    }
215}