iota_swarm_config/
test_utils.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use std::{collections::HashMap, sync::Arc};
6
7use iota_config::genesis::Genesis;
8use iota_sdk_types::crypto::{Intent, IntentMessage, IntentScope};
9use iota_types::{
10    base_types::{AuthorityName, ExecutionData},
11    committee::{Committee, EpochId, StakeUnit},
12    crypto::{
13        AuthorityKeyPair, AuthoritySignInfo, AuthoritySignature, IotaAuthoritySignature,
14        KeypairTraits,
15    },
16    messages_checkpoint::{
17        CertifiedCheckpointSummary, CheckpointDigest, CheckpointSequenceNumber, CheckpointSummary,
18        CheckpointVersionSpecificData, EndOfEpochData, FullCheckpointContents, VerifiedCheckpoint,
19        VerifiedCheckpointContents,
20    },
21};
22
23use crate::network_config::NetworkConfig;
24
25pub struct CommitteeFixture {
26    epoch: EpochId,
27    validators: HashMap<AuthorityName, (AuthorityKeyPair, StakeUnit)>,
28    committee: Committee,
29    genesis: Option<Arc<Genesis>>,
30}
31
32type MakeCheckpointResults = (
33    Vec<VerifiedCheckpoint>,
34    Vec<VerifiedCheckpointContents>,
35    HashMap<CheckpointSequenceNumber, CheckpointDigest>,
36    HashMap<CheckpointDigest, VerifiedCheckpoint>,
37);
38
39impl CommitteeFixture {
40    pub fn generate<R: ::rand::RngCore + ::rand::CryptoRng>(
41        mut rng: R,
42        epoch: EpochId,
43        committee_size: usize,
44    ) -> Self {
45        let validators = (0..committee_size)
46            .map(|_| iota_types::crypto::get_key_pair_from_rng::<AuthorityKeyPair, _>(&mut rng).1)
47            .map(|keypair| (keypair.public().into(), (keypair, 1)))
48            .collect::<HashMap<_, _>>();
49
50        let committee = Committee::new_for_testing_with_normalized_voting_power(
51            epoch,
52            validators
53                .iter()
54                .map(|(name, (_, stake))| (*name, *stake))
55                .collect(),
56        );
57
58        Self {
59            epoch,
60            validators,
61            committee,
62            genesis: None,
63        }
64    }
65
66    pub fn from_network_config(network_config: &NetworkConfig) -> Self {
67        let committee = network_config.genesis.committee().unwrap();
68        Self {
69            epoch: committee.epoch,
70            validators: committee
71                .members()
72                .map(|(name, stake)| {
73                    (
74                        *name,
75                        (
76                            network_config
77                                .validator_configs()
78                                .iter()
79                                .find(|config| config.authority_public_key() == *name)
80                                .unwrap()
81                                .authority_key_pair()
82                                .copy(),
83                            *stake,
84                        ),
85                    )
86                })
87                .collect(),
88            committee,
89            genesis: Some(Arc::new(network_config.genesis.clone())),
90        }
91    }
92
93    pub fn committee(&self) -> &Committee {
94        &self.committee
95    }
96
97    fn create_root_checkpoint(&self) -> (VerifiedCheckpoint, VerifiedCheckpointContents) {
98        assert_eq!(self.epoch, 0, "root checkpoint must be epoch 0");
99
100        let mut contents = empty_contents();
101        if let Some(genesis) = &self.genesis {
102            // if genesis is provided, create checkpoint contents with the genesis
103            // transaction
104            let tx = genesis.transaction().clone();
105            let effects = genesis.effects().clone();
106            let execution_data = ExecutionData::new(tx, effects);
107
108            contents = VerifiedCheckpointContents::new_unchecked(
109                FullCheckpointContents::new_with_causally_ordered_transactions(std::iter::once(
110                    execution_data,
111                )),
112            );
113        }
114
115        let content_digest = *contents
116            .clone()
117            .into_inner()
118            .into_checkpoint_contents()
119            .digest();
120
121        let checkpoint = CheckpointSummary {
122            epoch: 0,
123            sequence_number: 0,
124            network_total_transactions: contents.num_of_transactions() as u64,
125            content_digest,
126            previous_digest: None,
127            epoch_rolling_gas_cost_summary: Default::default(),
128            end_of_epoch_data: None,
129            timestamp_ms: 0,
130            version_specific_data: bcs::to_bytes(&CheckpointVersionSpecificData::empty_for_tests())
131                .unwrap(),
132            checkpoint_commitments: Default::default(),
133        };
134
135        (self.create_certified_checkpoint(checkpoint), contents)
136    }
137
138    fn create_certified_checkpoint(&self, checkpoint: CheckpointSummary) -> VerifiedCheckpoint {
139        let signatures = self
140            .validators
141            .iter()
142            .map(|(name, (key, _))| {
143                let intent_msg = IntentMessage::new(
144                    Intent::iota_app(IntentScope::CheckpointSummary),
145                    checkpoint.clone(),
146                );
147                let signature = AuthoritySignature::new_secure(&intent_msg, &checkpoint.epoch, key);
148                AuthoritySignInfo {
149                    epoch: checkpoint.epoch,
150                    authority: *name,
151                    signature,
152                }
153            })
154            .collect();
155
156        let checkpoint = CertifiedCheckpointSummary::new(checkpoint, signatures, self.committee())
157            .unwrap()
158            .try_into_verified(self.committee())
159            .unwrap();
160
161        checkpoint
162    }
163
164    pub fn make_random_checkpoints(
165        &self,
166        number_of_checkpoints: usize,
167        previous_checkpoint: Option<VerifiedCheckpoint>,
168    ) -> MakeCheckpointResults {
169        self.make_checkpoints(number_of_checkpoints, previous_checkpoint, random_contents)
170    }
171
172    pub fn make_empty_checkpoints(
173        &self,
174        number_of_checkpoints: usize,
175        previous_checkpoint: Option<VerifiedCheckpoint>,
176    ) -> MakeCheckpointResults {
177        self.make_checkpoints(number_of_checkpoints, previous_checkpoint, empty_contents)
178    }
179
180    fn make_checkpoints<F: Fn() -> VerifiedCheckpointContents>(
181        &self,
182        number_of_checkpoints: usize,
183        previous_checkpoint: Option<VerifiedCheckpoint>,
184        content_generator: F,
185    ) -> MakeCheckpointResults {
186        // Only skip the first one if it was supplied
187        let skip = previous_checkpoint.is_some() as usize;
188        let first = previous_checkpoint
189            .map(|c| (c, empty_contents()))
190            .unwrap_or_else(|| self.create_root_checkpoint());
191
192        let (ordered_checkpoints, contents): (Vec<_>, Vec<_>) =
193            std::iter::successors(Some(first), |prev| {
194                let contents = content_generator();
195                let contents_digest = *contents
196                    .clone()
197                    .into_inner()
198                    .into_checkpoint_contents()
199                    .digest();
200                let summary = CheckpointSummary {
201                    epoch: self.epoch,
202                    sequence_number: prev.0.sequence_number + 1,
203                    network_total_transactions: prev.0.network_total_transactions
204                        + contents.num_of_transactions() as u64,
205                    content_digest: contents_digest,
206                    previous_digest: Some(*prev.0.digest()),
207                    epoch_rolling_gas_cost_summary: Default::default(),
208                    end_of_epoch_data: None,
209                    timestamp_ms: 0,
210                    version_specific_data: bcs::to_bytes(
211                        &CheckpointVersionSpecificData::empty_for_tests(),
212                    )
213                    .unwrap(),
214                    checkpoint_commitments: Default::default(),
215                };
216
217                let checkpoint = self.create_certified_checkpoint(summary);
218
219                Some((checkpoint, contents))
220            })
221            .skip(skip)
222            .take(number_of_checkpoints)
223            .unzip();
224
225        let (sequence_number_to_digest, checkpoints) = ordered_checkpoints
226            .iter()
227            .cloned()
228            .map(|checkpoint| {
229                let digest = *checkpoint.digest();
230                ((checkpoint.sequence_number, digest), (digest, checkpoint))
231            })
232            .unzip();
233
234        (
235            ordered_checkpoints,
236            contents,
237            sequence_number_to_digest,
238            checkpoints,
239        )
240    }
241
242    pub fn make_end_of_epoch_checkpoint(
243        &self,
244        previous_checkpoint: VerifiedCheckpoint,
245        end_of_epoch_data: Option<EndOfEpochData>,
246    ) -> (
247        CheckpointSequenceNumber,
248        CheckpointDigest,
249        VerifiedCheckpoint,
250    ) {
251        let summary = CheckpointSummary {
252            epoch: self.epoch,
253            sequence_number: previous_checkpoint.sequence_number + 1,
254            network_total_transactions: 0,
255            content_digest: *empty_contents()
256                .into_inner()
257                .into_checkpoint_contents()
258                .digest(),
259            previous_digest: Some(*previous_checkpoint.digest()),
260            epoch_rolling_gas_cost_summary: Default::default(),
261            end_of_epoch_data,
262            timestamp_ms: 0,
263            version_specific_data: bcs::to_bytes(&CheckpointVersionSpecificData::empty_for_tests())
264                .unwrap(),
265            checkpoint_commitments: Default::default(),
266        };
267
268        let checkpoint = self.create_certified_checkpoint(summary);
269
270        (checkpoint.sequence_number, *checkpoint.digest(), checkpoint)
271    }
272}
273
274pub fn empty_contents() -> VerifiedCheckpointContents {
275    VerifiedCheckpointContents::new_unchecked(
276        FullCheckpointContents::new_with_causally_ordered_transactions(std::iter::empty()),
277    )
278}
279
280pub fn random_contents() -> VerifiedCheckpointContents {
281    VerifiedCheckpointContents::new_unchecked(FullCheckpointContents::random_for_testing())
282}