saito_core/core/util/test/
test_manager.rs

1#[cfg(test)]
2pub mod test {
3    //
4    // TestManager provides a set of functions to simplify the testing of dynamic
5    // interactions, such as chain-reorganizations and/or other tests that require
6    // the state of the chain itself to vary in complicated ways.
7    //
8    // Our goal with this file is to make it faster and easier to make tests more
9    // succinct, by providing helper functions that can create chains of blocks
10    // with valid transactions and add them to the blockchain in a systematic way
11    // that also permits manual intercession.
12    //
13    //  - create_block
14    //  - create_transaction
15    //  - create_transactions
16    //  - on_chain_reorganization
17    //
18    // generate_block 		<-- create a block
19    // generate_block_and_metadata 	<--- create block with metadata (difficulty, has_golden ticket, etc.)
20    // generate_transaction 	<-- create a transaction
21    // add_block 			<-- create and add block to longest_chain
22    // add_block_on_hash		<-- create and add block elsewhere on chain
23    // on_chain_reorganization 	<-- test monetary policy
24    //
25    //
26    use std::borrow::BorrowMut;
27    use std::error::Error;
28    use std::fmt::{Debug, Formatter};
29    use std::fs;
30    use std::io::{BufReader, Read, Write};
31    use std::ops::Deref;
32    use std::path::Path;
33    use std::sync::Arc;
34    use std::time::{SystemTime, UNIX_EPOCH};
35
36    use ahash::AHashMap;
37    use log::{debug, info};
38    use rand::rngs::OsRng;
39    use secp256k1::Secp256k1;
40    use tokio::sync::mpsc::{Receiver, Sender};
41    use tokio::sync::RwLock;
42
43    use crate::core::consensus::block::{Block, BlockType};
44    use crate::core::consensus::blockchain::{AddBlockResult, Blockchain};
45    use crate::core::consensus::golden_ticket::GoldenTicket;
46    use crate::core::consensus::mempool::Mempool;
47    use crate::core::consensus::peers::peer_collection::PeerCollection;
48    use crate::core::consensus::slip::Slip;
49    use crate::core::consensus::transaction::{Transaction, TransactionType};
50    use crate::core::consensus::wallet::Wallet;
51    use crate::core::defs::{
52        Currency, PrintForLog, SaitoHash, SaitoPrivateKey, SaitoPublicKey, SaitoSignature,
53        Timestamp, UtxoSet, NOLAN_PER_SAITO, PROJECT_PUBLIC_KEY,
54    };
55    use crate::core::io::network::Network;
56    use crate::core::io::storage::Storage;
57    use crate::core::mining_thread::MiningEvent;
58    use crate::core::process::keep_time::{KeepTime, Timer};
59    use crate::core::util::configuration::{
60        BlockchainConfig, Configuration, ConsensusConfig, PeerConfig, Server,
61    };
62    use crate::core::util::crypto::{generate_keys, generate_random_bytes, hash, verify_signature};
63    use crate::core::util::test::test_io_handler::test::TestIOHandler;
64
65    pub fn create_timestamp() -> Timestamp {
66        SystemTime::now()
67            .duration_since(UNIX_EPOCH)
68            .unwrap()
69            .as_millis() as Timestamp
70    }
71
72    pub const TEST_ISSUANCE_FILEPATH: &'static str = "../saito-rust/data/issuance/test/issuance";
73
74    struct TestTimeKeeper {}
75
76    impl KeepTime for TestTimeKeeper {
77        fn get_timestamp_in_ms(&self) -> Timestamp {
78            create_timestamp()
79        }
80    }
81
82    pub struct TestManager {
83        pub mempool_lock: Arc<RwLock<Mempool>>,
84        pub blockchain_lock: Arc<RwLock<Blockchain>>,
85        pub wallet_lock: Arc<RwLock<Wallet>>,
86        pub latest_block_hash: SaitoHash,
87        pub network: Network,
88        pub storage: Storage,
89        pub peer_lock: Arc<RwLock<PeerCollection>>,
90        pub sender_to_miner: Sender<MiningEvent>,
91        pub receiver_in_miner: Receiver<MiningEvent>,
92        pub config_lock: Arc<RwLock<dyn Configuration + Send + Sync>>,
93        pub issuance_path: &'static str,
94    }
95    impl Default for TestManager {
96        fn default() -> Self {
97            let keys = generate_keys();
98            let wallet = Wallet::new(keys.1, keys.0);
99            let peers = Arc::new(RwLock::new(PeerCollection::default()));
100            let wallet_lock = Arc::new(RwLock::new(wallet));
101            let blockchain_lock = Arc::new(RwLock::new(Blockchain::new(
102                wallet_lock.clone(),
103                100,
104                0,
105                60,
106            )));
107            let mempool_lock = Arc::new(RwLock::new(Mempool::new(wallet_lock.clone())));
108            let (sender_to_miner, receiver_in_miner) = tokio::sync::mpsc::channel(1000);
109            let configs = Arc::new(RwLock::new(TestConfiguration {
110                consensus: ConsensusConfig {
111                    genesis_period: 100,
112                    heartbeat_interval: 100,
113                    prune_after_blocks: 8,
114                    max_staker_recursions: 3,
115                    default_social_stake: 0,
116                    default_social_stake_period: 60,
117                },
118                blockchain: BlockchainConfig::default(),
119            }));
120
121            let issuance_path = TestManager::get_test_issuance_file().unwrap();
122
123            Self {
124                wallet_lock: wallet_lock.clone(),
125                blockchain_lock,
126                mempool_lock,
127                latest_block_hash: [0; 32],
128                network: Network::new(
129                    Box::new(TestIOHandler::new()),
130                    peers.clone(),
131                    wallet_lock.clone(),
132                    configs.clone(),
133                    Timer {
134                        time_reader: Arc::new(TestTimeKeeper {}),
135                        hasten_multiplier: 1,
136                        start_time: 0,
137                    },
138                ),
139                peer_lock: peers.clone(),
140                storage: Storage::new(Box::new(TestIOHandler::new())),
141                sender_to_miner: sender_to_miner.clone(),
142                receiver_in_miner,
143                config_lock: configs,
144                issuance_path,
145            }
146        }
147    }
148
149    impl TestManager {
150        pub fn get_test_issuance_file() -> Result<&'static str, std::io::Error> {
151            let temp_dir = Path::new("./temp_test_directory").to_path_buf();
152            fs::create_dir_all(&temp_dir)?;
153            let source_path = Path::new(TEST_ISSUANCE_FILEPATH);
154            // Read the existing counter from the file or initialize it to 1 if the file doesn't exist
155            let issuance_counter_path = temp_dir.join("issuance_counter.txt");
156            let counter = if issuance_counter_path.exists() {
157                let mut file = BufReader::new(fs::File::open(&issuance_counter_path)?);
158                let mut buffer = String::new();
159                file.read_to_string(&mut buffer)?;
160                buffer.trim().parse::<usize>().unwrap_or(1)
161            } else {
162                1
163            };
164            let target_filename = format!("issuance-{}.txt", counter);
165            let target_path = temp_dir.join(target_filename);
166            fs::copy(source_path, &target_path)?;
167            // Update the counter in the file for the next instance
168            let mut file = fs::File::create(&issuance_counter_path)?;
169            writeln!(file, "{}", counter + 1)?;
170
171            let target_path_str = target_path
172                .to_str()
173                .ok_or_else(|| std::io::Error::new(std::io::ErrorKind::Other, "Invalid path"))?;
174
175            let static_str: &'static str = Box::leak(target_path_str.to_string().into_boxed_str());
176
177            Ok(static_str)
178        }
179
180        pub async fn disable_staking(&mut self) {
181            let mut blockchain = self.blockchain_lock.write().await;
182            blockchain.social_stake_requirement = 0;
183        }
184
185        pub async fn enable_staking(&mut self, mut stake_value: Currency) {
186            let mut blockchain = self.blockchain_lock.write().await;
187            if stake_value == 0 {
188                stake_value = 2_000_000 * NOLAN_PER_SAITO;
189            }
190            blockchain.social_stake_requirement = stake_value;
191        }
192
193        pub async fn convert_issuance_to_hashmap(
194            &self,
195            filepath: &'static str,
196        ) -> AHashMap<SaitoPublicKey, u64> {
197            let buffer = self.storage.read(filepath).await.unwrap();
198            let content = String::from_utf8(buffer).expect("Failed to convert to String");
199
200            let mut issuance_map = AHashMap::new();
201            for line in content.lines() {
202                let parts: Vec<&str> = line.split_whitespace().collect();
203                if parts.len() >= 2 {
204                    let amount: u64 = match parts[0].parse() {
205                        Ok(num) => num,
206                        Err(_) => continue, // skip lines with invalid numbers
207                    };
208
209                    let address_key = if amount < 25000 {
210                        // Default public key when amount is less than 25000
211
212                        SaitoPublicKey::from_base58(PROJECT_PUBLIC_KEY)
213                            .expect("Failed to decode Base58")
214                    } else {
215                        SaitoPublicKey::from_base58(parts[1]).expect("Failed to decode Base58")
216                    };
217
218                    if address_key.len() == 33 {
219                        let mut address: [u8; 33] = [0; 33];
220                        address.copy_from_slice(&address_key);
221                        *issuance_map.entry(address).or_insert(0) += amount; // add the amount to the existing value or set it if not present
222                    }
223                }
224            }
225            issuance_map
226        }
227        pub async fn wait_for_mining_event(&mut self) {
228            self.receiver_in_miner
229                .recv()
230                .await
231                .expect("mining event receive failed");
232        }
233
234        /// add block to blockchain
235        pub async fn add_block(&mut self, block: Block) -> AddBlockResult {
236            debug!("adding block to test manager blockchain");
237
238            let configs = self.config_lock.write().await;
239            let mut blockchain = self.blockchain_lock.write().await;
240            let mut mempool = self.mempool_lock.write().await;
241
242            let result = blockchain
243                .add_block(block, &mut self.storage, &mut mempool, configs.deref())
244                .await;
245
246            self.latest_block_hash = blockchain.last_block_hash;
247            result
248        }
249
250        // check that the blockchain connects properly
251        pub async fn check_blockchain(&self) {
252            let blockchain = self.blockchain_lock.read().await;
253
254            for i in 1..blockchain.blocks.len() {
255                let block_hash = blockchain
256                    .blockring
257                    .get_longest_chain_block_hash_at_block_id(i as u64)
258                    .unwrap_or([0; 32]);
259
260                let previous_block_hash = blockchain
261                    .blockring
262                    .get_longest_chain_block_hash_at_block_id((i as u64) - 1)
263                    .unwrap_or([0; 32]);
264
265                let block = blockchain.get_block_sync(&block_hash);
266                let previous_block = blockchain.get_block_sync(&previous_block_hash);
267
268                if block_hash == [0; 32] {
269                    assert!(block.is_none());
270                } else {
271                    assert!(block.is_some());
272                    if i != 1 && previous_block_hash != [0; 32] {
273                        assert!(previous_block.is_some());
274                        assert_eq!(
275                            block.unwrap().previous_block_hash,
276                            previous_block.unwrap().hash
277                        );
278                    }
279                }
280            }
281        }
282
283        // check that everything spendable in the main UTXOSET is spendable on the longest
284        // chain and vice-versa.
285        pub async fn check_utxoset(&self) {
286            let blockchain = self.blockchain_lock.read().await;
287
288            let mut utxoset: UtxoSet = AHashMap::new();
289            let latest_block_id = blockchain.get_latest_block_id();
290            // let first_block_id = latest_block_id - GENESIS_PERIOD + 1;
291
292            info!("---- check utxoset ");
293            for i in 1..=latest_block_id {
294                let block_hash = blockchain
295                    .blockring
296                    .get_longest_chain_block_hash_at_block_id(i)
297                    .unwrap();
298                let mut block = blockchain.get_block(&block_hash).unwrap().clone();
299                block
300                    .upgrade_block_to_block_type(BlockType::Full, &self.storage, false)
301                    .await;
302                info!(
303                    "WINDING ID HASH - {:?} {:?} with txs : {:?} block_type : {:?}",
304                    block.id,
305                    block_hash.to_hex(),
306                    block.transactions.len(),
307                    block.block_type
308                );
309
310                for j in 0..block.transactions.len() {
311                    block.transactions[j].on_chain_reorganization(&mut utxoset, true);
312                    debug!(
313                        "from : {:?} to : {:?} utxo len : {:?}",
314                        block.transactions[j].from.len(),
315                        block.transactions[j].to.len(),
316                        utxoset.len()
317                    );
318                }
319            }
320
321            // check main utxoset matches longest-chain
322            for (key, value) in &blockchain.utxoset {
323                assert!(*value);
324                match utxoset.get(key) {
325                    Some(value2) => {
326                        // everything spendable in blockchain.utxoset should be spendable on longest-chain
327                        assert!(*value);
328                        assert_eq!(*value, *value2);
329                    }
330                    None => {
331                        let slip = Slip::parse_slip_from_utxokey(key).unwrap();
332                        info!(
333                            "block : {:?} tx : {:?} amount : {:?} type : {:?}",
334                            slip.block_id, slip.tx_ordinal, slip.amount, slip.slip_type
335                        );
336                        panic!("utxoset value should be false for key : {:?}. generated utxo size : {:?}. current utxo size : {:?}",
337                               key.to_hex(), utxoset.len(), blockchain.utxoset.len());
338                    }
339                }
340            }
341
342            // check longest-chain matches utxoset
343            for (key, value) in &utxoset {
344                //info!("{:?} / {}", key, value);
345                match blockchain.utxoset.get(key) {
346                    Some(value2) => {
347                        // everything spendable in longest-chain should be spendable on blockchain.utxoset
348                        if *value == true {
349                            //                        info!("comparing {} and {}", value, value2);
350                            assert_eq!(value, value2);
351                        } else {
352                            //
353                            // everything spent in longest-chain should be spendable on blockchain.utxoset
354                            //
355                            // if *value > 1 {
356                            //     //                            info!("comparing {} and {}", value, value2);
357                            //     assert_eq!(value, value2);
358                            // } else {
359                            //     //
360                            //     // unspendable (0) does not need to exist
361                            //     //
362                            // }
363                        }
364                    }
365                    None => {
366                        info!("comparing {:?} with expected value {}", key, value);
367                        info!("Value does not exist in actual blockchain!");
368                        assert_eq!(1, 2);
369                    }
370                }
371            }
372        }
373
374        pub async fn check_token_supply(&self) {
375            //
376            // the total supply of tokens in the network (fixed)
377            //
378            let mut token_supply: Currency = 0;
379            //
380            // the spendable supply includes the tokens that exist as UTXO which
381            // can be spent in any block. we have to track this over time by
382            // adjusting based on the amount that are removed from the UTXO set
383            // and the amount that is added back to it.
384            //
385            // tokens that do not exist in the spendable_supply must be in one
386            // of four places
387            //
388            // - block.treasury
389            // - block.graveyard
390            // - collected current block (N)
391            // - collected previous block (N-1)
392            //
393            // we cannot test the LAST block as we do not know with the ATR payout
394            // how much in fees were collected, but we can always test the PREVIOUS
395            // blockdance properly so that our inability to calculate the fees in each
396            // block through a simple inputs - outputs comparisons nonetheless
397            // works and resolves to the total token supply nonetheless.
398            //
399            let mut spendable_supply: Currency = 0;
400
401            let mut block_inputs: Currency;
402            let mut block_outputs: Currency;
403
404            let mut previous_block_treasury: Currency = 0;
405            let mut current_block_treasury: Currency = 0;
406            let mut previous_block_graveyard: Currency = 0;
407            let mut current_block_graveyard: Currency = 0;
408            let mut previous_block_previous_block_unpaid: Currency = 0;
409            let mut current_block_previous_block_unpaid: Currency = 0;
410            let mut current_block_missing_tokens: i128 = 0;
411            let mut previous_block_missing_tokens: i128 = 0;
412
413            let mut current_block_net_change_in_treasury: i128 = 0;
414            let mut current_block_net_change_in_graveyard: i128 = 0;
415            let mut current_block_net_change_in_utxo: i128 = 0;
416
417            let mut previous_block_net_change_in_treasury: i128 = 0;
418            let mut previous_block_net_change_in_graveyard: i128 = 0;
419            let mut previous_block_net_change_in_utxo: i128 = 0;
420
421            let mut amount_of_tokens_unaccounted_for: i128 = 0;
422
423            let mut block_contains_fee_tx: bool;
424            let mut block_fee_tx_index: usize = 0;
425
426            let blockchain = self.blockchain_lock.read().await;
427
428            let latest_block_id = blockchain.get_latest_block_id();
429
430            for i in 1..=latest_block_id {
431                let block_hash = blockchain
432                    .blockring
433                    .get_longest_chain_block_hash_at_block_id(i)
434                    .unwrap();
435                let block = blockchain.get_block(&block_hash).unwrap();
436
437                block_inputs = 0;
438                block_outputs = 0;
439                block_contains_fee_tx = false;
440
441                previous_block_treasury = current_block_treasury;
442                current_block_treasury = block.treasury;
443
444                for t in 0..block.transactions.len() {
445                    //
446                    // the difference in the treasury
447                    //
448                    if block.transactions[t].transaction_type == TransactionType::Fee {
449                        block_contains_fee_tx = true;
450                        block_fee_tx_index = t;
451                    }
452
453                    //
454                    // add up inputs and outputs
455                    //
456                    for z in 0..block.transactions[t].from.len() {
457                        block_inputs += block.transactions[t].from[z].amount;
458                    }
459                    for z in 0..block.transactions[t].to.len() {
460                        block_outputs += block.transactions[t].to[z].amount;
461                    }
462                }
463
464                //
465                // block #1 sets circulation
466                //
467                if i == 1 {
468                    token_supply = block_outputs + block.graveyard + block.treasury;
469                    spendable_supply = block_outputs;
470                    current_block_treasury = block.treasury;
471                    current_block_graveyard = block.graveyard;
472                    current_block_previous_block_unpaid = 0;
473                } else {
474                    println!(
475                        "block {:?} -> bi {:?} and bo: {:?}",
476                        i, block_inputs, block_outputs
477                    );
478
479                    //
480                    // update current variables
481                    //
482                    current_block_treasury = block.treasury;
483                    current_block_graveyard = block.graveyard;
484                    current_block_previous_block_unpaid = block.previous_block_unpaid;
485
486                    //
487                    // calculate the net change, could be positive or negative
488                    //
489                    current_block_net_change_in_treasury =
490                        current_block_treasury as i128 - previous_block_treasury as i128;
491                    current_block_net_change_in_graveyard =
492                        current_block_graveyard as i128 - previous_block_graveyard as i128;
493                    current_block_net_change_in_utxo = block_outputs as i128 - block_inputs as i128;
494
495                    //
496                    // spendable supply adjusted
497                    //
498                    let new_spendable_supply =
499                        spendable_supply as i128 + current_block_net_change_in_utxo as i128;
500
501                    //
502                    // how many tokens are unaccounted for?
503                    //
504                    // the unknown is the amount collected THIS block, as it is masked by the
505                    // changes in the treasury. we cannot determine how much is going to show up
506                    // in the unpaid amount NEXT block.
507                    //
508                    //
509                    current_block_missing_tokens = token_supply as i128
510                        - new_spendable_supply as i128
511                        - current_block_treasury as i128
512                        - current_block_graveyard as i128
513                        - current_block_previous_block_unpaid as i128;
514
515                    amount_of_tokens_unaccounted_for =
516                        token_supply as i128 - new_spendable_supply as i128;
517
518                    //
519                    // what should be missing are fees + unpaid
520                    //
521                    println!("block i : {:?}", i);
522                    println!("total_supply : {:?}", token_supply);
523                    println!("spendable supply (start): {:?}", spendable_supply);
524                    println!("spendable supply (close): {:?}", new_spendable_supply);
525                    println!("block_outputs: {:?}", block_outputs);
526                    println!("block_inputs : {:?}", block_inputs);
527                    println!(
528                        "current_block_net_change_in_treasury : {:?}",
529                        current_block_net_change_in_treasury
530                    );
531                    println!(
532                        "current_block_net_change_in_graveyard : {:?}",
533                        current_block_net_change_in_graveyard
534                    );
535                    println!(
536                        "current_block_net_change_in_utxo : {:?}",
537                        current_block_net_change_in_utxo
538                    );
539                    println!(
540                        "current_block_previous_block_unpaid : {:?}",
541                        current_block_previous_block_unpaid
542                    );
543                    println!(
544                        "previous_block_net_change_in_treasury : {:?}",
545                        previous_block_net_change_in_treasury
546                    );
547                    println!(
548                        "previous_block_net_change_in_graveyard : {:?}",
549                        previous_block_net_change_in_graveyard
550                    );
551                    println!(
552                        "previous_block_net_change_in_utxo : {:?}",
553                        previous_block_net_change_in_utxo
554                    );
555                    println!(
556                        "previous_block_previous_block_unpaid : {:?}",
557                        previous_block_previous_block_unpaid
558                    );
559                    println!(
560                        "current block missing tokens : {:?}",
561                        current_block_missing_tokens
562                    );
563                    println!(
564                        "previous block missing tokens : {:?}",
565                        previous_block_missing_tokens
566                    );
567                    println!("----------------------------------------");
568
569                    //
570                    // two variables swim together -- the staking treasury, and the fees collected
571                    //
572
573                    //
574                    // run our test!
575                    //
576                    assert_eq!(
577                        token_supply, spendable_supply,
578                        "token_supply : {:?} spendable_supply : {:?}",
579                        token_supply, spendable_supply
580                    );
581
582                    //
583                    // prepare variables for next loop
584                    //
585                    spendable_supply = new_spendable_supply as u64;
586
587                    previous_block_treasury = current_block_treasury;
588                    previous_block_graveyard = current_block_graveyard;
589                    previous_block_previous_block_unpaid = current_block_previous_block_unpaid;
590                    previous_block_missing_tokens = current_block_missing_tokens;
591
592                    previous_block_net_change_in_treasury = current_block_net_change_in_treasury;
593                    previous_block_net_change_in_graveyard = current_block_net_change_in_graveyard;
594                    previous_block_net_change_in_utxo = current_block_net_change_in_utxo;
595                }
596            }
597        }
598
599        // create block
600        pub async fn create_block(
601            &mut self,
602            parent_hash: SaitoHash,
603            timestamp: Timestamp,
604            txs_count: usize,
605            txs_amount: Currency,
606            txs_fee: Currency,
607            include_valid_golden_ticket: bool,
608        ) -> Block {
609            let is_staking_enabled;
610            {
611                let blockchain = self.blockchain_lock.read().await;
612                is_staking_enabled = blockchain.social_stake_requirement != 0;
613            }
614
615            self.create_block_with_staking(
616                parent_hash,
617                timestamp,
618                txs_count,
619                txs_amount,
620                txs_fee,
621                include_valid_golden_ticket,
622                is_staking_enabled,
623            )
624            .await
625        }
626        pub async fn create_block_with_staking(
627            &mut self,
628            parent_hash: SaitoHash,
629            timestamp: Timestamp,
630            txs_count: usize,
631            txs_amount: Currency,
632            txs_fee: Currency,
633            include_valid_golden_ticket: bool,
634            with_staking_tx: bool,
635        ) -> Block {
636            let mut transactions: AHashMap<SaitoSignature, Transaction> = Default::default();
637            let private_key: SaitoPrivateKey;
638            let public_key: SaitoPublicKey;
639
640            let configs = self.config_lock.read().await;
641            let genesis_period = configs.get_consensus_config().unwrap().genesis_period;
642
643            let (latest_block_id, latest_block_hash) = {
644                let blockchain = self.blockchain_lock.read().await;
645                let latest_block_id = blockchain.blockring.get_latest_block_id();
646                let latest_block_hash = blockchain.blockring.get_latest_block_hash();
647                (latest_block_id, latest_block_hash)
648            };
649
650            {
651                let wallet = self.wallet_lock.read().await;
652
653                public_key = wallet.public_key;
654                private_key = wallet.private_key;
655            }
656
657            for _i in 0..txs_count {
658                let mut transaction;
659                {
660                    let mut wallet = self.wallet_lock.write().await;
661
662                    transaction = Transaction::create(
663                        &mut wallet,
664                        public_key,
665                        txs_amount,
666                        txs_fee,
667                        false,
668                        None,
669                        latest_block_id,
670                        genesis_period,
671                    )
672                    .unwrap();
673                }
674
675                transaction.sign(&private_key);
676                transaction.generate(&public_key, 0, 0);
677                transactions.insert(transaction.signature, transaction);
678            }
679
680            if include_valid_golden_ticket {
681                let blockchain = self.blockchain_lock.read().await;
682
683                let block = blockchain.get_block(&parent_hash).unwrap_or_else(|| {
684                    panic!("couldn't find block for hash : {:?}", parent_hash.to_hex())
685                });
686                let golden_ticket: GoldenTicket = Self::create_golden_ticket(
687                    self.wallet_lock.clone(),
688                    parent_hash,
689                    block.difficulty,
690                )
691                .await;
692                let mut gttx: Transaction;
693                {
694                    let wallet = self.wallet_lock.read().await;
695
696                    gttx = Wallet::create_golden_ticket_transaction(
697                        golden_ticket,
698                        &wallet.public_key,
699                        &wallet.private_key,
700                    )
701                    .await;
702                }
703                gttx.generate(&public_key, 0, 0);
704                transactions.insert(gttx.signature, gttx);
705            }
706            if with_staking_tx {
707                let blockchain = self.blockchain_lock.read().await;
708                let mut wallet = self.wallet_lock.write().await;
709                let result = wallet.create_staking_transaction(
710                    blockchain.social_stake_requirement,
711                    blockchain.get_latest_unlocked_stake_block_id(),
712                    (blockchain.get_latest_block_id() + 1)
713                        .saturating_sub(configs.get_consensus_config().unwrap().genesis_period),
714                );
715                assert!(result.is_ok());
716                let mut tx = result.unwrap();
717                tx.generate(&public_key, 0, 0);
718                transactions.insert(tx.signature, tx);
719            }
720
721            let mut blockchain = self.blockchain_lock.write().await;
722
723            // create block
724            let mut block = Block::create(
725                &mut transactions,
726                parent_hash,
727                blockchain.borrow_mut(),
728                timestamp,
729                &public_key,
730                &private_key,
731                None,
732                configs.deref(),
733                &self.storage,
734            )
735            .await
736            .unwrap();
737            block.generate().unwrap();
738            block.sign(&private_key);
739
740            block
741        }
742        pub async fn create_golden_ticket(
743            wallet_lock: Arc<RwLock<Wallet>>,
744            block_hash: SaitoHash,
745            block_difficulty: u64,
746        ) -> GoldenTicket {
747            let public_key;
748            {
749                let wallet = wallet_lock.read().await;
750
751                public_key = wallet.public_key;
752            }
753            let mut random_bytes = hash(&generate_random_bytes(32).await);
754
755            let mut gt = GoldenTicket::create(block_hash, random_bytes, public_key);
756
757            while !gt.validate(block_difficulty) {
758                random_bytes = hash(&generate_random_bytes(32).await);
759                gt = GoldenTicket::create(block_hash, random_bytes, public_key);
760            }
761
762            GoldenTicket::new(block_hash, random_bytes, public_key)
763        }
764
765        pub async fn get_balance(&self) -> u64 {
766            let wallet_lock = self.get_wallet_lock();
767            let wallet = wallet_lock.read().await;
768            let my_balance = wallet.get_available_balance();
769
770            my_balance
771        }
772
773        pub fn get_mempool_lock(&self) -> Arc<RwLock<Mempool>> {
774            return self.mempool_lock.clone();
775        }
776
777        pub fn get_wallet_lock(&self) -> Arc<RwLock<Wallet>> {
778            return self.wallet_lock.clone();
779        }
780
781        // pub async fn get_wallet(&self) -> tokio::sync::RwLockReadGuard<'_, Wallet> {
782        //     let wallet;
783        //     let _wallet_;
784        //     wallet = self.wallet_lock.read().await;
785        //     // return wallet;
786        // }
787
788        pub fn get_blockchain_lock(&self) -> Arc<RwLock<Blockchain>> {
789            return self.blockchain_lock.clone();
790        }
791
792        pub async fn get_latest_block_hash(&self) -> SaitoHash {
793            let blockchain = self.blockchain_lock.read().await;
794            blockchain.blockring.get_latest_block_hash()
795        }
796        pub async fn get_latest_block(&self) -> Block {
797            let blockchain = self.blockchain_lock.read().await;
798            let block = blockchain
799                .get_latest_block()
800                .expect("latest block should exist")
801                .clone();
802            block
803        }
804
805        pub async fn initialize(&mut self, issuance_transactions: u64, issuance_amount: Currency) {
806            let timestamp = create_timestamp();
807            self.initialize_with_timestamp(issuance_transactions, issuance_amount, timestamp)
808                .await;
809        }
810
811        // initialize chain from slips and add some amount my public key
812        //
813        pub async fn initialize_from_slips_and_value(&mut self, slips: Vec<Slip>, amount: u64) {
814            self.disable_staking().await;
815            // reset data dirs
816            let _ = tokio::fs::remove_dir_all("data/blocks").await;
817            tokio::fs::create_dir_all("data/blocks").await.unwrap();
818            let _ = tokio::fs::remove_dir_all("data/wallets").await;
819            tokio::fs::create_dir_all("data/wallets").await.unwrap();
820
821            let private_key: SaitoPrivateKey;
822            let my_public_key: SaitoPublicKey;
823            {
824                let wallet = self.wallet_lock.read().await;
825                private_key = wallet.private_key;
826                my_public_key = wallet.public_key;
827            }
828
829            // create first block
830            let timestamp = create_timestamp();
831            let mut block = self.create_block([0; 32], timestamp, 0, 0, 0, false).await;
832
833            for slip in slips {
834                let mut tx: Transaction =
835                    Transaction::create_issuance_transaction(slip.public_key, slip.amount);
836                tx.generate(&slip.public_key, 0, 0);
837                tx.sign(&private_key);
838                block.add_transaction(tx);
839            }
840
841            // add some value to my own public key
842            let mut tx = Transaction::create_issuance_transaction(my_public_key, amount);
843
844            tx.generate(&my_public_key, 0, 0);
845            tx.sign(&private_key);
846            block.add_transaction(tx);
847
848            {
849                let configs = self.config_lock.read().await;
850                block.merkle_root =
851                    block.generate_merkle_root(configs.is_browser(), configs.is_spv_mode());
852            }
853            block.generate().unwrap();
854            block.sign(&private_key);
855
856            assert!(verify_signature(
857                &block.pre_hash,
858                &block.signature,
859                &block.creator,
860            ));
861
862            // and add first block to blockchain
863            self.add_block(block).await;
864        }
865
866        // initialize chain from just slips properties
867        pub async fn initialize_from_slips(&mut self, slips: Vec<Slip>) {
868            self.disable_staking().await;
869            // initialize timestamp
870            let timestamp = create_timestamp();
871            // reset data dirs
872            let _ = tokio::fs::remove_dir_all("data/blocks").await;
873            tokio::fs::create_dir_all("data/blocks").await.unwrap();
874            let _ = tokio::fs::remove_dir_all("data/wallets").await;
875            tokio::fs::create_dir_all("data/wallets").await.unwrap();
876
877            // create initial transactions
878            let private_key: SaitoPrivateKey;
879            {
880                let wallet = self.wallet_lock.read().await;
881                private_key = wallet.private_key;
882            }
883
884            // create first block
885            let mut block = self.create_block([0; 32], timestamp, 0, 0, 0, false).await;
886
887            // generate UTXO-carrying VIP transactions
888            for slip in slips {
889                let mut tx = Transaction::create_issuance_transaction(slip.public_key, slip.amount);
890                tx.generate(&slip.public_key, 0, 0);
891                tx.sign(&private_key);
892                block.add_transaction(tx);
893            }
894
895            {
896                let configs = self.config_lock.read().await;
897                // we have added VIP, so need to regenerate the merkle-root
898                block.merkle_root =
899                    block.generate_merkle_root(configs.is_browser(), configs.is_spv_mode());
900            }
901            block.generate().unwrap();
902            block.sign(&private_key);
903
904            assert!(verify_signature(
905                &block.pre_hash,
906                &block.signature,
907                &block.creator,
908            ));
909
910            // and add first block to blockchain
911            self.add_block(block).await;
912        }
913        pub async fn initialize_with_timestamp(
914            &mut self,
915            issuance_transactions: u64,
916            issuance_amount: Currency,
917            timestamp: Timestamp,
918        ) {
919            self.disable_staking().await;
920            // reset data dirs
921            let _ = tokio::fs::remove_dir_all("data/blocks").await;
922            let _ = tokio::fs::create_dir_all("data/blocks").await;
923            let _ = tokio::fs::remove_dir_all("data/wallets").await;
924            let _ = tokio::fs::create_dir_all("data/wallets").await;
925
926            // create initial transactions
927            let private_key: SaitoPrivateKey;
928            let public_key: SaitoPublicKey;
929            {
930                let wallet = self.wallet_lock.read().await;
931
932                public_key = wallet.public_key;
933                private_key = wallet.private_key;
934            }
935
936            // create first block
937            let mut block = self.create_block([0; 32], timestamp, 0, 0, 0, false).await;
938
939            // generate UTXO-carrying transactions
940            for _i in 0..issuance_transactions {
941                let mut tx = Transaction::create_issuance_transaction(public_key, issuance_amount);
942                tx.generate(&public_key, 0, 0);
943                tx.sign(&private_key);
944                block.add_transaction(tx);
945            }
946
947            {
948                let configs = self.config_lock.read().await;
949                // we have added txs, so need to regenerate the merkle-root
950                block.merkle_root =
951                    block.generate_merkle_root(configs.is_browser(), configs.is_spv_mode());
952            }
953            block.generate().unwrap();
954            block.sign(&private_key);
955
956            assert!(verify_signature(
957                &block.pre_hash,
958                &block.signature,
959                &block.creator,
960            ));
961
962            // and add first block to blockchain
963            self.add_block(block).await;
964        }
965
966        //create a genesis block for testing
967        pub async fn create_test_gen_block(&mut self, amount: u64) {
968            debug!("create_test_gen_block");
969            let wallet_read = self.wallet_lock.read().await;
970            let mut tx = Transaction::create_issuance_transaction(wallet_read.public_key, amount);
971            tx.sign(&wallet_read.private_key);
972            drop(wallet_read);
973            let configs = self.config_lock.read().await;
974
975            let mut blockchain = self.blockchain_lock.write().await;
976            let mut mempool = self.mempool_lock.write().await;
977
978            mempool
979                .add_transaction_if_validates(tx.clone(), &blockchain)
980                .await;
981
982            let timestamp = create_timestamp();
983
984            let genblock: Block = mempool
985                .bundle_genesis_block(&mut blockchain, timestamp, configs.deref(), &self.storage)
986                .await;
987            let _res = blockchain
988                .add_block(genblock, &mut self.storage, &mut mempool, configs.deref())
989                .await;
990        }
991
992        //convenience function assuming longest chain
993        pub async fn balance_map(&mut self) -> AHashMap<SaitoPublicKey, u64> {
994            let blockchain = self.blockchain_lock.write().await;
995
996            let mut utxo_balances: AHashMap<SaitoPublicKey, u64> = AHashMap::new();
997
998            let latest_id = blockchain.get_latest_block_id();
999            for i in 1..=latest_id {
1000                let block_hash = blockchain
1001                    .blockring
1002                    .get_longest_chain_block_hash_at_block_id(i as u64)
1003                    .unwrap();
1004                let block = blockchain.get_block(&block_hash).unwrap().clone();
1005                for j in 0..block.transactions.len() {
1006                    let tx = &block.transactions[j];
1007
1008                    tx.from.iter().for_each(|input| {
1009                        utxo_balances
1010                            .entry(input.public_key)
1011                            .and_modify(|e| *e -= input.amount)
1012                            .or_insert(0);
1013                    });
1014
1015                    tx.to.iter().for_each(|output| {
1016                        utxo_balances
1017                            .entry(output.public_key)
1018                            .and_modify(|e| *e += output.amount)
1019                            .or_insert(output.amount);
1020                    });
1021                }
1022            }
1023            utxo_balances
1024        }
1025
1026        pub async fn transfer_value_to_public_key(
1027            &mut self,
1028            to_public_key: SaitoPublicKey,
1029            amount: u64,
1030            timestamp_addition: u64,
1031        ) -> Result<(), Box<dyn Error>> {
1032            info!(
1033                "transferring value : {:?} to : {:?}",
1034                amount,
1035                to_public_key.to_hex()
1036            );
1037
1038            let genesis_period;
1039            {
1040                let configs = self.config_lock.read().await;
1041                genesis_period = configs.get_consensus_config().unwrap().genesis_period;
1042            }
1043
1044            let (latest_block_id, latest_block_hash) = {
1045                let blockchain = self.blockchain_lock.read().await;
1046                let latest_block_id = blockchain.blockring.get_latest_block_id();
1047                let latest_block_hash = blockchain.blockring.get_latest_block_hash();
1048                (latest_block_id, latest_block_hash)
1049            };
1050
1051            let timestamp = create_timestamp();
1052
1053            let mut block = self
1054                .create_block(
1055                    latest_block_hash,
1056                    timestamp + timestamp_addition,
1057                    0,
1058                    0,
1059                    0,
1060                    false,
1061                )
1062                .await;
1063
1064            let private_key;
1065
1066            {
1067                let wallet = self.wallet_lock.read().await;
1068                private_key = wallet.private_key;
1069                let mut tx = Transaction::create(
1070                    &mut wallet.clone(),
1071                    to_public_key,
1072                    amount,
1073                    0,
1074                    false,
1075                    None,
1076                    latest_block_id,
1077                    genesis_period,
1078                )?;
1079                tx.sign(&private_key);
1080                block.add_transaction(tx);
1081            }
1082
1083            {
1084                let configs = self.config_lock.read().await;
1085
1086                block.merkle_root =
1087                    block.generate_merkle_root(configs.is_browser(), configs.is_spv_mode());
1088            }
1089
1090            block.generate().unwrap();
1091            block.sign(&private_key);
1092
1093            assert!(verify_signature(
1094                &block.pre_hash,
1095                &block.signature,
1096                &block.creator,
1097            ));
1098            self.add_block(block).await;
1099
1100            Ok(())
1101        }
1102
1103        pub fn generate_random_public_key() -> SaitoPublicKey {
1104            let secp = Secp256k1::new();
1105            let (_secret_key, public_key) = secp.generate_keypair(&mut OsRng);
1106            let serialized_key: SaitoPublicKey = public_key.serialize();
1107            serialized_key
1108        }
1109    }
1110
1111    impl Drop for TestManager {
1112        fn drop(&mut self) {
1113            // Cleanup: Remove the temporary directory and its contents
1114            if let Err(err) = fs::remove_dir_all("./temp_test_directory") {
1115                eprintln!("Error cleaning up: {}", err);
1116            }
1117        }
1118    }
1119
1120    struct TestConfiguration {
1121        consensus: ConsensusConfig,
1122        blockchain: BlockchainConfig,
1123    }
1124
1125    impl Debug for TestConfiguration {
1126        fn fmt(&self, _f: &mut Formatter<'_>) -> std::fmt::Result {
1127            todo!()
1128        }
1129    }
1130
1131    impl Configuration for TestConfiguration {
1132        fn get_server_configs(&self) -> Option<&Server> {
1133            todo!()
1134        }
1135
1136        fn get_peer_configs(&self) -> &Vec<PeerConfig> {
1137            todo!()
1138        }
1139
1140        fn get_blockchain_configs(&self) -> &BlockchainConfig {
1141            &self.blockchain
1142        }
1143
1144        fn get_block_fetch_url(&self) -> String {
1145            todo!()
1146        }
1147
1148        fn is_spv_mode(&self) -> bool {
1149            false
1150        }
1151
1152        fn is_browser(&self) -> bool {
1153            false
1154        }
1155
1156        fn replace(&mut self, _config: &dyn Configuration) {
1157            todo!()
1158        }
1159
1160        fn get_consensus_config(&self) -> Option<&ConsensusConfig> {
1161            Some(&self.consensus)
1162        }
1163    }
1164}