dg_xch_cli_lib/simulator/
mod.rs

1pub mod chain_user;
2
3use crate::simulator::chain_user::ChainUser;
4use crate::wallets::memory_wallet::{MemoryWallet, MemoryWalletConfig, MemoryWalletStore};
5use crate::wallets::{Wallet, WalletInfo};
6use bip39::Mnemonic;
7use dg_xch_clients::api::simulator::SimulatorAPI;
8use dg_xch_clients::rpc::simulator::SimulatorClient;
9use dg_xch_core::blockchain::sized_bytes::Bytes32;
10use dg_xch_core::blockchain::wallet_type::WalletType;
11use dg_xch_core::consensus::constants::{ConsensusConstants, SIMULATOR};
12use dg_xch_keys::{decode_puzzle_hash, key_from_mnemonic};
13use lazy_static::lazy_static;
14use std::collections::HashMap;
15use std::io::{Error, ErrorKind};
16use std::sync::atomic::{AtomicBool, Ordering};
17use std::sync::Arc;
18use std::time::Duration;
19use tokio::sync::Mutex;
20use tokio::task::JoinHandle;
21use tokio::time;
22
23lazy_static! {
24    pub static ref UTIL_ADDRESS: Bytes32 =
25        decode_puzzle_hash("xch1ye5dzd44kkatnxx2je4s2agpwtqds5lsm5mlyef7plum5danxalq2dnqap")
26            .unwrap();
27}
28
29pub struct Simulator<'a> {
30    network: ConsensusConstants,
31    client: SimulatorClient,
32    run: Arc<AtomicBool>,
33    background: Mutex<Option<JoinHandle<()>>>,
34    users: Mutex<HashMap<String, Arc<ChainUser<'a>>>>,
35}
36impl<'a> Simulator<'a> {
37    pub fn new(
38        host: &str,
39        port: u16,
40        timeout: u64,
41        additional_headers: &Option<HashMap<String, String>>,
42        network: Option<ConsensusConstants>,
43    ) -> Result<Self, Error> {
44        Ok(Self {
45            network: network.unwrap_or((**SIMULATOR).clone()),
46            client: SimulatorClient::new(host, port, timeout, additional_headers)?,
47            run: Arc::new(AtomicBool::new(false)),
48            background: Mutex::new(None),
49            users: Mutex::new(HashMap::default()),
50        })
51    }
52    pub fn client(&self) -> &SimulatorClient {
53        &self.client
54    }
55    pub fn constants(&self) -> &ConsensusConstants {
56        &self.network
57    }
58    pub async fn get_user(&self, name: &str) -> Option<Arc<ChainUser<'a>>> {
59        self.users.lock().await.get(name).cloned()
60    }
61    pub async fn new_user(
62        &'a self,
63        name: &str,
64        menmonic: Option<String>,
65    ) -> Result<Arc<ChainUser<'a>>, Error> {
66        let mut map_lock = self.users.lock().await;
67        if map_lock.contains_key(name) {
68            return Err(Error::new(ErrorKind::AlreadyExists, "User already exists"));
69        }
70        let mnemonic = match menmonic {
71            Some(s) => Mnemonic::parse(s).map_err(Error::other)?,
72            None => Mnemonic::generate(24).map_err(Error::other)?,
73        };
74        let secret_key = key_from_mnemonic(&mnemonic).map_err(Error::other)?;
75        let chain_user = Arc::new(ChainUser {
76            simulator: self,
77            wallet: Arc::new(MemoryWallet::create_simulator(
78                WalletInfo {
79                    id: 0,
80                    name: format!("{name}'s Wallet"),
81                    wallet_type: WalletType::StandardWallet,
82                    constants: Arc::new(self.network.clone()),
83                    master_sk: secret_key.clone(),
84                    wallet_store: Arc::new(Mutex::new(MemoryWalletStore::new(secret_key, 0))),
85                    data: String::new(),
86                },
87                MemoryWalletConfig {
88                    fullnode_host: self.client.host.clone(),
89                    fullnode_port: self.client.port,
90                    fullnode_ssl_path: None,
91                    additional_headers: self.client.additional_headers.clone(),
92                },
93            )?),
94            name: name.to_string(),
95        });
96        map_lock.insert(name.to_string(), chain_user.clone());
97        Ok(chain_user)
98    }
99    pub async fn next_blocks(&self, blocks: i64, call_per_block: bool) -> Result<(), Error> {
100        if call_per_block {
101            for _ in 0..blocks {
102                self.client.farm_blocks(*UTIL_ADDRESS, 1, true).await?;
103            }
104        } else {
105            self.client.farm_blocks(*UTIL_ADDRESS, blocks, true).await?;
106        }
107        Ok(())
108    }
109    pub async fn farm_coins(
110        &self,
111        address: Bytes32,
112        blocks: i64,
113        transaction_block: bool,
114    ) -> Result<(), Error> {
115        self.client
116            .farm_blocks(address, blocks, transaction_block)
117            .await
118            .map_err(Into::into)
119            .map(|_| ())
120    }
121    pub async fn is_auto_farming(&self) -> Result<bool, Error> {
122        self.client
123            .get_auto_farming()
124            .await
125            .map_err(Into::into)
126            .map(|r| r.auto_farm_enabled)
127    }
128    pub async fn run(&self, block_interval: Option<Duration>) -> Result<(), Error> {
129        let mut block_interval = time::interval(block_interval.unwrap_or(Duration::from_secs(19)));
130        let run = self.run.clone();
131        if let Some(background) = &mut *self.background.lock().await {
132            if run.load(Ordering::Relaxed) {
133                return Err(Error::new(
134                    ErrorKind::AlreadyExists,
135                    "Simulator Already Running",
136                ));
137            }
138            run.store(false, Ordering::Relaxed);
139            background.await?;
140        }
141        run.store(true, Ordering::Relaxed);
142        *self.background.lock().await = Some(tokio::spawn(async move {
143            while run.load(Ordering::Relaxed) {
144                block_interval.tick().await;
145            }
146        }));
147        Ok(())
148    }
149    pub async fn stop(&self) -> Result<(), Error> {
150        self.run.store(false, Ordering::Relaxed);
151        if let Some(background) = &mut *self.background.lock().await {
152            background.await?;
153            Ok(())
154        } else {
155            Err(Error::new(
156                ErrorKind::AlreadyExists,
157                "Simulator Not Running",
158            ))
159        }
160    }
161}