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, OpCert};
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(
206        &self,
207        _opcert: &OpCert,
208    ) -> Result<Option<KesPeriod>, ChainObserverError> {
209        Ok(Some(0))
210    }
211}
212
213#[cfg(test)]
214mod tests {
215    use mithril_common::test_utils::fake_data;
216
217    use super::*;
218
219    #[tokio::test]
220    async fn test_get_current_epoch() {
221        let time_point = TimePoint::dummy();
222        let fake_observer = FakeChainObserver::new(Some(time_point.clone()));
223        let current_epoch = fake_observer.get_current_epoch().await.unwrap();
224
225        assert_eq!(Some(time_point.epoch), current_epoch);
226    }
227
228    #[tokio::test]
229    async fn test_get_current_chain_point() {
230        let fake_observer = FakeChainObserver::new(None);
231        fake_observer.set_current_time_point(Some(TimePoint::dummy())).await;
232        let chain_point = fake_observer.get_current_chain_point().await.unwrap();
233
234        assert_eq!(
235            Some(TimePoint::dummy().chain_point),
236            chain_point,
237            "get current chain point should not fail"
238        );
239    }
240
241    #[tokio::test]
242    async fn test_get_current_stake_distribution() {
243        let fake_observer = FakeChainObserver::new(None);
244        fake_observer.set_signers(fake_data::signers_with_stakes(2)).await;
245        let stake_distribution = fake_observer.get_current_stake_distribution().await;
246
247        assert_eq!(
248            2,
249            stake_distribution.unwrap().unwrap().len(),
250            "get current stake distribution should not fail and should not be empty"
251        );
252    }
253
254    #[tokio::test]
255    async fn test_get_current_datums() {
256        let fake_address = "addr_test_123456".to_string();
257        let fake_datums =
258            vec![TxDatum("tx_datum_1".to_string()), TxDatum("tx_datum_2".to_string())];
259        let fake_observer = FakeChainObserver::new(None);
260        fake_observer.set_datums(fake_datums.clone()).await;
261        let datums = fake_observer
262            .get_current_datums(&fake_address)
263            .await
264            .expect("get_current_datums should not fail");
265
266        assert_eq!(fake_datums, datums);
267    }
268
269    #[tokio::test]
270    async fn test_increase_block_number() {
271        let fake_observer = FakeChainObserver::new(None);
272        fake_observer.set_current_time_point(Some(TimePoint::dummy())).await;
273        fake_observer.increase_block_number(375).await;
274
275        let chain_point = fake_observer.get_current_chain_point().await.unwrap();
276        assert_eq!(
277            Some(ChainPoint {
278                block_number: TimePoint::dummy().chain_point.block_number + 375,
279                ..TimePoint::dummy().chain_point
280            }),
281            chain_point,
282            "get current chain point should not fail"
283        );
284    }
285
286    #[tokio::test]
287    async fn test_decrease_block_number() {
288        let fake_observer = FakeChainObserver::new(None);
289        fake_observer
290            .set_current_time_point(Some(TimePoint {
291                chain_point: ChainPoint {
292                    block_number: BlockNumber(1000),
293                    ..TimePoint::dummy().chain_point
294                },
295                ..TimePoint::dummy()
296            }))
297            .await;
298        fake_observer.decrease_block_number(800).await;
299
300        let chain_point = fake_observer.get_current_chain_point().await.unwrap();
301        assert_eq!(
302            Some(ChainPoint {
303                block_number: BlockNumber(200),
304                ..TimePoint::dummy().chain_point
305            }),
306            chain_point,
307            "get current chain point should not fail"
308        );
309    }
310
311    #[tokio::test]
312    async fn test_increase_slot_number() {
313        let fake_observer = FakeChainObserver::new(None);
314        fake_observer.set_current_time_point(Some(TimePoint::dummy())).await;
315        fake_observer.increase_slot_number(375).await;
316
317        let chain_point = fake_observer.get_current_chain_point().await.unwrap();
318        assert_eq!(
319            Some(ChainPoint {
320                slot_number: TimePoint::dummy().chain_point.slot_number + 375,
321                ..TimePoint::dummy().chain_point
322            }),
323            chain_point,
324            "get current chain point should not fail"
325        );
326    }
327
328    #[tokio::test]
329    async fn test_decrease_slot_number() {
330        let fake_observer = FakeChainObserver::new(None);
331        fake_observer
332            .set_current_time_point(Some(TimePoint {
333                chain_point: ChainPoint {
334                    slot_number: SlotNumber(1000),
335                    ..TimePoint::dummy().chain_point
336                },
337                ..TimePoint::dummy()
338            }))
339            .await;
340        fake_observer.decrease_slot_number(800).await;
341
342        let chain_point = fake_observer.get_current_chain_point().await.unwrap();
343        assert_eq!(
344            Some(ChainPoint {
345                slot_number: SlotNumber(200),
346                ..TimePoint::dummy().chain_point
347            }),
348            chain_point,
349            "get current chain point should not fail"
350        );
351    }
352
353    #[tokio::test]
354    async fn test_get_current_era() {
355        let fake_observer = FakeChainObserver::new(None);
356
357        let current_era = fake_observer
358            .get_current_era()
359            .await
360            .expect("get_current_era should not fail");
361        assert_ne!(Some("Conway".to_string()), current_era);
362
363        fake_observer.set_current_era("Conway".to_string()).await;
364        let current_era = fake_observer
365            .get_current_era()
366            .await
367            .expect("get_current_era should not fail");
368        assert_eq!(Some("Conway".to_string()), current_era);
369    }
370}