mithril_common/chain_observer/
fake_observer.rs

1use async_trait::async_trait;
2use tokio::sync::RwLock;
3
4use crate::chain_observer::interface::*;
5use crate::chain_observer::{ChainAddress, TxDatum};
6use crate::crypto_helper::{KESPeriod, OpCert};
7use crate::{entities::*, test_utils::fake_data};
8
9/// A Fake [ChainObserver] for testing purpose using fixed data.
10pub struct FakeObserver {
11    /// A list of [SignerWithStake], used for [get_current_stake_distribution].
12    ///
13    /// [get_current_stake_distribution]: ChainObserver::get_current_stake_distribution
14    pub signers: RwLock<Vec<SignerWithStake>>,
15
16    /// A [TimePoint], used by [get_current_epoch]
17    ///
18    /// [get_current_epoch]: ChainObserver::get_current_epoch
19    pub current_time_point: RwLock<Option<TimePoint>>,
20
21    /// A list of [TxDatum], used by [get_current_datums]
22    ///
23    /// [get_current_datums]: ChainObserver::get_current_datums
24    pub datums: RwLock<Vec<TxDatum>>,
25
26    /// A Cardano era, used by [get_current_era]
27    ///
28    /// [get_current_era]: ChainObserver::get_current_era
29    pub current_era: RwLock<String>,
30}
31
32impl FakeObserver {
33    /// FakeObserver factory
34    pub fn new(current_time_point: Option<TimePoint>) -> Self {
35        Self {
36            signers: RwLock::new(vec![]),
37            current_time_point: RwLock::new(current_time_point.clone()),
38            datums: RwLock::new(vec![]),
39            current_era: RwLock::new(String::new()),
40        }
41    }
42
43    /// Increase by one the epoch of the [current_time_point][`FakeObserver::current_time_point`].
44    pub async fn next_epoch(&self) -> Option<Epoch> {
45        let mut current_time_point = self.current_time_point.write().await;
46        *current_time_point = current_time_point.as_ref().map(|time_point| TimePoint {
47            epoch: time_point.epoch + 1,
48            ..time_point.clone()
49        });
50
51        current_time_point.as_ref().map(|b| b.epoch)
52    }
53
54    /// Increase the block number of the [current_time_point][`FakeObserver::current_time_point`] by
55    /// the given increment.
56    pub async fn increase_block_number(&self, increment: u64) -> Option<BlockNumber> {
57        self.change_block_number(|actual_block_number| actual_block_number + increment)
58            .await
59    }
60
61    /// Decrease the block number of the [current_time_point][`FakeObserver::current_time_point`] by
62    /// the given decrement.
63    pub async fn decrease_block_number(&self, decrement: u64) -> Option<BlockNumber> {
64        self.change_block_number(|actual_block_number| actual_block_number - decrement)
65            .await
66    }
67
68    async fn change_block_number(
69        &self,
70        change_to_apply: impl Fn(BlockNumber) -> BlockNumber,
71    ) -> Option<BlockNumber> {
72        let mut current_time_point = self.current_time_point.write().await;
73
74        *current_time_point = current_time_point.as_ref().map(|time_point| TimePoint {
75            chain_point: ChainPoint {
76                block_number: change_to_apply(time_point.chain_point.block_number),
77                ..time_point.chain_point.clone()
78            },
79            ..time_point.clone()
80        });
81
82        current_time_point
83            .as_ref()
84            .map(|b| b.chain_point.block_number)
85    }
86
87    /// Increase the slot number of the [current_time_point][`FakeObserver::current_time_point`] by
88    /// the given increment.
89    pub async fn increase_slot_number(&self, increment: u64) -> Option<SlotNumber> {
90        self.change_slot_number(|actual_slot_number| actual_slot_number + increment)
91            .await
92    }
93
94    /// Decrease the slot number of the [current_time_point][`FakeObserver::current_time_point`] by
95    /// the given decrement.
96    pub async fn decrease_slot_number(&self, decrement: u64) -> Option<SlotNumber> {
97        self.change_slot_number(|actual_slot_number| actual_slot_number - decrement)
98            .await
99    }
100
101    async fn change_slot_number(
102        &self,
103        change_to_apply: impl Fn(SlotNumber) -> SlotNumber,
104    ) -> Option<SlotNumber> {
105        let mut current_time_point = self.current_time_point.write().await;
106
107        *current_time_point = current_time_point.as_ref().map(|time_point| TimePoint {
108            chain_point: ChainPoint {
109                slot_number: change_to_apply(time_point.chain_point.slot_number),
110                ..time_point.chain_point.clone()
111            },
112            ..time_point.clone()
113        });
114
115        current_time_point
116            .as_ref()
117            .map(|b| b.chain_point.slot_number)
118    }
119
120    /// Set the signers that will use to compute the result of
121    /// [get_current_stake_distribution][ChainObserver::get_current_stake_distribution].
122    pub async fn set_signers(&self, new_signers: Vec<SignerWithStake>) {
123        let mut signers = self.signers.write().await;
124        *signers = new_signers;
125    }
126
127    /// Set the time point
128    pub async fn set_current_time_point(&self, new_current_time_point: Option<TimePoint>) {
129        let mut current_time_point = self.current_time_point.write().await;
130        *current_time_point = new_current_time_point;
131    }
132
133    /// Set the datums that will use to compute the result of
134    /// [get_current_datums][ChainObserver::get_current_datums].
135    pub async fn set_datums(&self, new_datums: Vec<TxDatum>) {
136        let mut datums = self.datums.write().await;
137        *datums = new_datums;
138    }
139
140    /// Set the current Era
141    /// [get_current_era][ChainObserver::get_current_era].
142    pub async fn set_current_era(&self, new_current_era: String) {
143        let mut current_era = self.current_era.write().await;
144        *current_era = new_current_era;
145    }
146}
147
148impl Default for FakeObserver {
149    fn default() -> Self {
150        let mut observer = Self::new(Some(TimePoint::dummy()));
151        observer.signers = RwLock::new(fake_data::signers_with_stakes(2));
152
153        observer
154    }
155}
156
157#[async_trait]
158impl ChainObserver for FakeObserver {
159    async fn get_current_datums(
160        &self,
161        _address: &ChainAddress,
162    ) -> Result<Vec<TxDatum>, ChainObserverError> {
163        let datums = self.datums.read().await;
164        Ok(datums.to_vec())
165    }
166
167    async fn get_current_era(&self) -> Result<Option<String>, ChainObserverError> {
168        Ok(Some(self.current_era.read().await.clone()))
169    }
170
171    async fn get_current_epoch(&self) -> Result<Option<Epoch>, ChainObserverError> {
172        Ok(self
173            .current_time_point
174            .read()
175            .await
176            .as_ref()
177            .map(|time_point| time_point.epoch))
178    }
179
180    async fn get_current_chain_point(&self) -> Result<Option<ChainPoint>, ChainObserverError> {
181        Ok(self
182            .current_time_point
183            .read()
184            .await
185            .as_ref()
186            .map(|time_point| time_point.chain_point.clone()))
187    }
188
189    async fn get_current_stake_distribution(
190        &self,
191    ) -> Result<Option<StakeDistribution>, ChainObserverError> {
192        Ok(Some(
193            self.signers
194                .read()
195                .await
196                .iter()
197                .map(|signer| (signer.party_id.clone() as PartyId, signer.stake as Stake))
198                .collect::<StakeDistribution>(),
199        ))
200    }
201
202    async fn get_current_kes_period(
203        &self,
204        _opcert: &OpCert,
205    ) -> Result<Option<KESPeriod>, ChainObserverError> {
206        Ok(Some(0))
207    }
208}
209
210#[cfg(test)]
211mod tests {
212    use crate::test_utils::fake_data;
213
214    use super::*;
215
216    #[tokio::test]
217    async fn test_get_current_epoch() {
218        let time_point = TimePoint::dummy();
219        let fake_observer = FakeObserver::new(Some(time_point.clone()));
220        let current_epoch = fake_observer.get_current_epoch().await.unwrap();
221
222        assert_eq!(Some(time_point.epoch), current_epoch);
223    }
224
225    #[tokio::test]
226    async fn test_get_current_chain_point() {
227        let fake_observer = FakeObserver::new(None);
228        fake_observer
229            .set_current_time_point(Some(TimePoint::dummy()))
230            .await;
231        let chain_point = fake_observer.get_current_chain_point().await.unwrap();
232
233        assert_eq!(
234            Some(TimePoint::dummy().chain_point),
235            chain_point,
236            "get current chain point should not fail"
237        );
238    }
239
240    #[tokio::test]
241    async fn test_get_current_stake_distribution() {
242        let fake_observer = FakeObserver::new(None);
243        fake_observer
244            .set_signers(fake_data::signers_with_stakes(2))
245            .await;
246        let stake_distribution = fake_observer.get_current_stake_distribution().await;
247
248        assert_eq!(
249            2,
250            stake_distribution.unwrap().unwrap().len(),
251            "get current stake distribution should not fail and should not be empty"
252        );
253    }
254
255    #[tokio::test]
256    async fn test_get_current_datums() {
257        let fake_address = "addr_test_123456".to_string();
258        let fake_datums = vec![
259            TxDatum("tx_datum_1".to_string()),
260            TxDatum("tx_datum_2".to_string()),
261        ];
262        let fake_observer = FakeObserver::new(None);
263        fake_observer.set_datums(fake_datums.clone()).await;
264        let datums = fake_observer
265            .get_current_datums(&fake_address)
266            .await
267            .expect("get_current_datums should not fail");
268
269        assert_eq!(fake_datums, datums);
270    }
271
272    #[tokio::test]
273    async fn test_increase_block_number() {
274        let fake_observer = FakeObserver::new(None);
275        fake_observer
276            .set_current_time_point(Some(TimePoint::dummy()))
277            .await;
278        fake_observer.increase_block_number(375).await;
279
280        let chain_point = fake_observer.get_current_chain_point().await.unwrap();
281        assert_eq!(
282            Some(ChainPoint {
283                block_number: TimePoint::dummy().chain_point.block_number + 375,
284                ..TimePoint::dummy().chain_point
285            }),
286            chain_point,
287            "get current chain point should not fail"
288        );
289    }
290
291    #[tokio::test]
292    async fn test_decrease_block_number() {
293        let fake_observer = FakeObserver::new(None);
294        fake_observer
295            .set_current_time_point(Some(TimePoint {
296                chain_point: ChainPoint {
297                    block_number: BlockNumber(1000),
298                    ..TimePoint::dummy().chain_point
299                },
300                ..TimePoint::dummy()
301            }))
302            .await;
303        fake_observer.decrease_block_number(800).await;
304
305        let chain_point = fake_observer.get_current_chain_point().await.unwrap();
306        assert_eq!(
307            Some(ChainPoint {
308                block_number: BlockNumber(200),
309                ..TimePoint::dummy().chain_point
310            }),
311            chain_point,
312            "get current chain point should not fail"
313        );
314    }
315
316    #[tokio::test]
317    async fn test_increase_slot_number() {
318        let fake_observer = FakeObserver::new(None);
319        fake_observer
320            .set_current_time_point(Some(TimePoint::dummy()))
321            .await;
322        fake_observer.increase_slot_number(375).await;
323
324        let chain_point = fake_observer.get_current_chain_point().await.unwrap();
325        assert_eq!(
326            Some(ChainPoint {
327                slot_number: TimePoint::dummy().chain_point.slot_number + 375,
328                ..TimePoint::dummy().chain_point
329            }),
330            chain_point,
331            "get current chain point should not fail"
332        );
333    }
334
335    #[tokio::test]
336    async fn test_decrease_slot_number() {
337        let fake_observer = FakeObserver::new(None);
338        fake_observer
339            .set_current_time_point(Some(TimePoint {
340                chain_point: ChainPoint {
341                    slot_number: SlotNumber(1000),
342                    ..TimePoint::dummy().chain_point
343                },
344                ..TimePoint::dummy()
345            }))
346            .await;
347        fake_observer.decrease_slot_number(800).await;
348
349        let chain_point = fake_observer.get_current_chain_point().await.unwrap();
350        assert_eq!(
351            Some(ChainPoint {
352                slot_number: SlotNumber(200),
353                ..TimePoint::dummy().chain_point
354            }),
355            chain_point,
356            "get current chain point should not fail"
357        );
358    }
359
360    #[tokio::test]
361    async fn test_get_current_era() {
362        let fake_observer = FakeObserver::new(None);
363
364        let current_era = fake_observer
365            .get_current_era()
366            .await
367            .expect("get_current_era should not fail");
368        assert_ne!(Some("Conway".to_string()), current_era);
369
370        fake_observer.set_current_era("Conway".to_string()).await;
371        let current_era = fake_observer
372            .get_current_era()
373            .await
374            .expect("get_current_era should not fail");
375        assert_eq!(Some("Conway".to_string()), current_era);
376    }
377}