mithril_cardano_node_chain/test/double/
chain_observer.rs

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