1#[cfg(test)]
2pub mod test {
3 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 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 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, };
208
209 let address_key = if amount < 25000 {
210 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; }
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 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 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 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 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 for (key, value) in &blockchain.utxoset {
323 assert!(*value);
324 match utxoset.get(key) {
325 Some(value2) => {
326 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 for (key, value) in &utxoset {
344 match blockchain.utxoset.get(key) {
346 Some(value2) => {
347 if *value == true {
349 assert_eq!(value, value2);
351 } else {
352 }
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 let mut token_supply: Currency = 0;
379 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 if block.transactions[t].transaction_type == TransactionType::Fee {
449 block_contains_fee_tx = true;
450 block_fee_tx_index = t;
451 }
452
453 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 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 current_block_treasury = block.treasury;
483 current_block_graveyard = block.graveyard;
484 current_block_previous_block_unpaid = block.previous_block_unpaid;
485
486 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 let new_spendable_supply =
499 spendable_supply as i128 + current_block_net_change_in_utxo as i128;
500
501 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 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 assert_eq!(
577 token_supply, spendable_supply,
578 "token_supply : {:?} spendable_supply : {:?}",
579 token_supply, spendable_supply
580 );
581
582 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 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 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 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 pub async fn initialize_from_slips_and_value(&mut self, slips: Vec<Slip>, amount: u64) {
814 self.disable_staking().await;
815 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 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 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 self.add_block(block).await;
864 }
865
866 pub async fn initialize_from_slips(&mut self, slips: Vec<Slip>) {
868 self.disable_staking().await;
869 let timestamp = create_timestamp();
871 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 let private_key: SaitoPrivateKey;
879 {
880 let wallet = self.wallet_lock.read().await;
881 private_key = wallet.private_key;
882 }
883
884 let mut block = self.create_block([0; 32], timestamp, 0, 0, 0, false).await;
886
887 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 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 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 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 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 let mut block = self.create_block([0; 32], timestamp, 0, 0, 0, false).await;
938
939 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 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 self.add_block(block).await;
964 }
965
966 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 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 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}