use async_trait::async_trait;
use tokio::sync::RwLock;
use crate::chain_observer::interface::*;
use crate::chain_observer::{ChainAddress, TxDatum};
use crate::crypto_helper::{KESPeriod, OpCert};
use crate::{entities::*, test_utils::fake_data};
pub struct FakeObserver {
pub signers: RwLock<Vec<SignerWithStake>>,
pub current_time_point: RwLock<Option<TimePoint>>,
pub datums: RwLock<Vec<TxDatum>>,
pub current_era: RwLock<String>,
}
impl FakeObserver {
pub fn new(current_time_point: Option<TimePoint>) -> Self {
Self {
signers: RwLock::new(vec![]),
current_time_point: RwLock::new(current_time_point.clone()),
datums: RwLock::new(vec![]),
current_era: RwLock::new(String::new()),
}
}
pub async fn next_epoch(&self) -> Option<Epoch> {
let mut current_time_point = self.current_time_point.write().await;
*current_time_point = current_time_point.as_ref().map(|time_point| TimePoint {
epoch: time_point.epoch + 1,
..time_point.clone()
});
current_time_point.as_ref().map(|b| b.epoch)
}
pub async fn increase_block_number(&self, increment: u64) -> Option<BlockNumber> {
self.change_block_number(|actual_block_number| actual_block_number + increment)
.await
}
pub async fn decrease_block_number(&self, decrement: u64) -> Option<BlockNumber> {
self.change_block_number(|actual_block_number| actual_block_number - decrement)
.await
}
async fn change_block_number(
&self,
change_to_apply: impl Fn(BlockNumber) -> BlockNumber,
) -> Option<BlockNumber> {
let mut current_time_point = self.current_time_point.write().await;
*current_time_point = current_time_point.as_ref().map(|time_point| TimePoint {
chain_point: ChainPoint {
block_number: change_to_apply(time_point.chain_point.block_number),
..time_point.chain_point.clone()
},
..time_point.clone()
});
current_time_point
.as_ref()
.map(|b| b.chain_point.block_number)
}
pub async fn increase_slot_number(&self, increment: u64) -> Option<SlotNumber> {
self.change_slot_number(|actual_slot_number| actual_slot_number + increment)
.await
}
pub async fn decrease_slot_number(&self, decrement: u64) -> Option<SlotNumber> {
self.change_slot_number(|actual_slot_number| actual_slot_number - decrement)
.await
}
async fn change_slot_number(
&self,
change_to_apply: impl Fn(SlotNumber) -> SlotNumber,
) -> Option<SlotNumber> {
let mut current_time_point = self.current_time_point.write().await;
*current_time_point = current_time_point.as_ref().map(|time_point| TimePoint {
chain_point: ChainPoint {
slot_number: change_to_apply(time_point.chain_point.slot_number),
..time_point.chain_point.clone()
},
..time_point.clone()
});
current_time_point
.as_ref()
.map(|b| b.chain_point.slot_number)
}
pub async fn set_signers(&self, new_signers: Vec<SignerWithStake>) {
let mut signers = self.signers.write().await;
*signers = new_signers;
}
pub async fn set_current_time_point(&self, new_current_time_point: Option<TimePoint>) {
let mut current_time_point = self.current_time_point.write().await;
*current_time_point = new_current_time_point;
}
pub async fn set_datums(&self, new_datums: Vec<TxDatum>) {
let mut datums = self.datums.write().await;
*datums = new_datums;
}
pub async fn set_current_era(&self, new_current_era: String) {
let mut current_era = self.current_era.write().await;
*current_era = new_current_era;
}
}
impl Default for FakeObserver {
fn default() -> Self {
let mut observer = Self::new(Some(TimePoint::dummy()));
observer.signers = RwLock::new(fake_data::signers_with_stakes(2));
observer
}
}
#[async_trait]
impl ChainObserver for FakeObserver {
async fn get_current_datums(
&self,
_address: &ChainAddress,
) -> Result<Vec<TxDatum>, ChainObserverError> {
let datums = self.datums.read().await;
Ok(datums.to_vec())
}
async fn get_current_era(&self) -> Result<Option<String>, ChainObserverError> {
Ok(Some(self.current_era.read().await.clone()))
}
async fn get_current_epoch(&self) -> Result<Option<Epoch>, ChainObserverError> {
Ok(self
.current_time_point
.read()
.await
.as_ref()
.map(|time_point| time_point.epoch))
}
async fn get_current_chain_point(&self) -> Result<Option<ChainPoint>, ChainObserverError> {
Ok(self
.current_time_point
.read()
.await
.as_ref()
.map(|time_point| time_point.chain_point.clone()))
}
async fn get_current_stake_distribution(
&self,
) -> Result<Option<StakeDistribution>, ChainObserverError> {
Ok(Some(
self.signers
.read()
.await
.iter()
.map(|signer| (signer.party_id.clone() as PartyId, signer.stake as Stake))
.collect::<StakeDistribution>(),
))
}
async fn get_current_kes_period(
&self,
_opcert: &OpCert,
) -> Result<Option<KESPeriod>, ChainObserverError> {
Ok(Some(0))
}
}
#[cfg(test)]
mod tests {
use crate::test_utils::fake_data;
use super::*;
#[tokio::test]
async fn test_get_current_epoch() {
let time_point = TimePoint::dummy();
let fake_observer = FakeObserver::new(Some(time_point.clone()));
let current_epoch = fake_observer.get_current_epoch().await.unwrap();
assert_eq!(Some(time_point.epoch), current_epoch);
}
#[tokio::test]
async fn test_get_current_chain_point() {
let fake_observer = FakeObserver::new(None);
fake_observer
.set_current_time_point(Some(TimePoint::dummy()))
.await;
let chain_point = fake_observer.get_current_chain_point().await.unwrap();
assert_eq!(
Some(TimePoint::dummy().chain_point),
chain_point,
"get current chain point should not fail"
);
}
#[tokio::test]
async fn test_get_current_stake_distribution() {
let fake_observer = FakeObserver::new(None);
fake_observer
.set_signers(fake_data::signers_with_stakes(2))
.await;
let stake_distribution = fake_observer.get_current_stake_distribution().await;
assert_eq!(
2,
stake_distribution.unwrap().unwrap().len(),
"get current stake distribution should not fail and should not be empty"
);
}
#[tokio::test]
async fn test_get_current_datums() {
let fake_address = "addr_test_123456".to_string();
let fake_datums = vec![
TxDatum("tx_datum_1".to_string()),
TxDatum("tx_datum_2".to_string()),
];
let fake_observer = FakeObserver::new(None);
fake_observer.set_datums(fake_datums.clone()).await;
let datums = fake_observer
.get_current_datums(&fake_address)
.await
.expect("get_current_datums should not fail");
assert_eq!(fake_datums, datums);
}
#[tokio::test]
async fn test_increase_block_number() {
let fake_observer = FakeObserver::new(None);
fake_observer
.set_current_time_point(Some(TimePoint::dummy()))
.await;
fake_observer.increase_block_number(375).await;
let chain_point = fake_observer.get_current_chain_point().await.unwrap();
assert_eq!(
Some(ChainPoint {
block_number: TimePoint::dummy().chain_point.block_number + 375,
..TimePoint::dummy().chain_point
}),
chain_point,
"get current chain point should not fail"
);
}
#[tokio::test]
async fn test_decrease_block_number() {
let fake_observer = FakeObserver::new(None);
fake_observer
.set_current_time_point(Some(TimePoint {
chain_point: ChainPoint {
block_number: BlockNumber(1000),
..TimePoint::dummy().chain_point
},
..TimePoint::dummy()
}))
.await;
fake_observer.decrease_block_number(800).await;
let chain_point = fake_observer.get_current_chain_point().await.unwrap();
assert_eq!(
Some(ChainPoint {
block_number: BlockNumber(200),
..TimePoint::dummy().chain_point
}),
chain_point,
"get current chain point should not fail"
);
}
#[tokio::test]
async fn test_increase_slot_number() {
let fake_observer = FakeObserver::new(None);
fake_observer
.set_current_time_point(Some(TimePoint::dummy()))
.await;
fake_observer.increase_slot_number(375).await;
let chain_point = fake_observer.get_current_chain_point().await.unwrap();
assert_eq!(
Some(ChainPoint {
slot_number: TimePoint::dummy().chain_point.slot_number + 375,
..TimePoint::dummy().chain_point
}),
chain_point,
"get current chain point should not fail"
);
}
#[tokio::test]
async fn test_decrease_slot_number() {
let fake_observer = FakeObserver::new(None);
fake_observer
.set_current_time_point(Some(TimePoint {
chain_point: ChainPoint {
slot_number: SlotNumber(1000),
..TimePoint::dummy().chain_point
},
..TimePoint::dummy()
}))
.await;
fake_observer.decrease_slot_number(800).await;
let chain_point = fake_observer.get_current_chain_point().await.unwrap();
assert_eq!(
Some(ChainPoint {
slot_number: SlotNumber(200),
..TimePoint::dummy().chain_point
}),
chain_point,
"get current chain point should not fail"
);
}
#[tokio::test]
async fn test_get_current_era() {
let fake_observer = FakeObserver::new(None);
let current_era = fake_observer
.get_current_era()
.await
.expect("get_current_era should not fail");
assert_ne!(Some("Conway".to_string()), current_era);
fake_observer.set_current_era("Conway".to_string()).await;
let current_era = fake_observer
.get_current_era()
.await
.expect("get_current_era should not fail");
assert_eq!(Some("Conway".to_string()), current_era);
}
}