1use bc_db::mongo_utils;
8use block_chain::block::Block;
9use block_chain::blockchain::Blockchain;
10use block_chain::wallet::{Transaction, Wallet};
11use bson::Document;
12use chrono::Utc;
13use mongodb::Collection;
14use serde_json::json;
15use std::collections::{BTreeMap, HashMap, VecDeque};
16use std::error::Error;
17use uuid::Uuid;
18
19pub fn list_blockchain(blockchain: &Blockchain) -> String {
23 let response = json!({
25 "message": "Listing Blockchain...",
26 "is_valid": blockchain.is_chain_valid(),
27 "length": blockchain.chain.len(),
28 "mempool_size": blockchain.mempool.len(),
29 "blockchain": blockchain });
31
32 serde_json::to_string_pretty(&response).expect("Failed to serialize response to JSON")
34}
35
36pub fn view_wallet(wallet: &Wallet, wallet_name: &str) -> String {
40 let response = json!({
42 "message": format!("Listing Wallet {}...", wallet_name),
43 "pending transactions": wallet.pending_transactions.len(),
44 "wallet": wallet });
46
47 serde_json::to_string_pretty(&response).expect("Failed to serialize response to JSON")
49}
50
51pub async fn load_blockchain(
55 blocks_collection: &Collection<Document>,
56 blockchain_collection: &Collection<Document>,
57) -> Blockchain {
58 let result =
60 mongo_utils::load_blockchain(blocks_collection.clone(), blockchain_collection.clone())
61 .await;
62 result.unwrap()
63}
64
65pub async fn get_wallet_addresses(wallets_collection: &Collection<Document>) -> Vec<String> {
69 let wallet_map: HashMap<String, Wallet> = Result::expect(
70 mongo_utils::load_wallets(wallets_collection.clone()).await,
71 "Could not get wallets data",
72 );
73
74 let mut addresses: Vec<String> = wallet_map
75 .iter()
76 .map(|(_key, wallet)| wallet.address.clone())
77 .collect();
78 addresses.sort();
79 addresses
80}
81
82pub async fn get_pending_transactions(wallets_collection: &Collection<Document>) -> Vec<String> {
86 let wallet_map: HashMap<String, Wallet> = Result::expect(
87 mongo_utils::load_wallets(wallets_collection.clone()).await,
88 "Could not get wallets data",
89 );
90
91 let pending_txrn_map: BTreeMap<String, VecDeque<Transaction>> = wallet_map
92 .into_iter()
93 .filter(|(_key, wallet)| !wallet.pending_transactions.is_empty())
94 .map(|(_key, wallet)| (wallet.address.clone(), wallet.pending_transactions.clone()))
95 .collect();
96
97 if pending_txrn_map.len() == 0 {
98 let mut no_trxns = Vec::new();
99 no_trxns.push(String::from(" No Pending Transactions! "));
100 return no_trxns;
101 }
102
103 pending_txrn_map
104 .values()
105 .flat_map(|deque| deque.iter())
106 .map(|transaction| format!("{:?}", transaction)) .collect()
108}
109
110pub async fn get_previous_hash<'a>(
114 blocks_collection: &'a Collection<Document>,
115 blockchain_collection: &'a Collection<Document>,
116) -> String {
117 let blockchain = load_blockchain(&blocks_collection, &blockchain_collection).await;
119 let previous_hash: String = blockchain.get_last_hash().unwrap().to_string();
120 previous_hash
121}
122
123pub async fn get_transaction(
127 tx_id: String,
128 wallets_collection: Collection<Document>,
129) -> Transaction {
130 let result = get_pending_txrn(&wallets_collection.clone()).await;
131
132 let mut transaction = None;
133 for (_address, transactions) in &result {
134 for txn in transactions.iter() {
135 println!("txn.tx_id {} and given tx_id is {}", txn.tx_id, tx_id);
136 println!("txn is {:?}", Some(txn));
137 if txn.tx_id.to_string() == tx_id.to_string() {
138 transaction = Some(txn);
139 }
140 }
141 }
142 transaction.unwrap().clone()
143}
144
145pub async fn send_crypto(
149 amount: f64,
150 fee: f64,
151 blockchain: &mut Blockchain,
152 sender_wallet: &mut Wallet,
153 receiver_wallet: &mut Wallet,
154 blocks_collection: Collection<Document>,
155 wallets_collection: Collection<Document>,
156 blockchain_collection: Collection<Document>,
157) -> Result<String, Box<dyn Error>> {
158 let (is_valid, msg) = validate_txn(blockchain, sender_wallet, amount, fee).await;
160 if is_valid == -1 {
161 return Ok(msg);
162 }
163
164 sender_wallet.update_balance(-amount - fee);
166 let transaction = Transaction {
169 tx_id: Uuid::new_v4().to_string(),
170 sender: sender_wallet.address.clone(),
171 receiver: receiver_wallet.address.clone(),
172 amount,
173 fee,
174 timestamp: Utc::now().timestamp() as u64,
175 };
176
177 let mut transactions: Vec<Transaction> = Vec::new();
178 transactions.push(transaction.clone());
179
180 sender_wallet.add_pending_transaction(transaction.clone());
181
182 blockchain.mempool.push(transaction);
183
184 save_to_db(
185 blockchain,
186 sender_wallet,
187 receiver_wallet,
188 blocks_collection,
189 wallets_collection,
190 blockchain_collection,
191 )
192 .await?;
193
194 Ok(String::from(
195 "Transaction successful, blockchain and wallets updated",
196 ))
197}
198
199pub async fn get_pending_txrn(
203 wallets_collection: &Collection<Document>,
204) -> BTreeMap<String, VecDeque<Transaction>> {
205 let wallet_map: HashMap<String, Wallet> = mongo_utils::load_wallets(wallets_collection.clone())
206 .await
207 .expect("Could not get wallets data");
208
209 let pending_txrn_map: BTreeMap<String, VecDeque<Transaction>> = wallet_map
210 .into_iter()
211 .filter(|(_key, wallet)| !wallet.pending_transactions.is_empty())
212 .map(|(_key, wallet)| (wallet.address.clone(), wallet.pending_transactions.clone()))
213 .collect();
214
215 pending_txrn_map
216}
217
218pub async fn mine_crypto(
222 blocks_collection: Collection<Document>,
223 blockchain_collection: Collection<Document>,
224) {
225 let blockchain = load_blockchain(&blocks_collection, &blockchain_collection).await;
227 let previous_hash: &str = blockchain.get_last_hash().unwrap();
228
229 let result = mine_transactions("The", previous_hash, 1).await;
231 let (nonce, hash, msg) = match result {
232 Ok((nonce, hash, formatted_message)) => (nonce, hash, formatted_message),
233 Err(e) => {
234 println!("Error occurred: {}", e);
235 return;
236 }
237 };
238 println!("Nonce: {nonce}, hash: {hash}\n{msg}");
239}
240
241pub async fn commit_mine_crypto(
245 tx_id: String,
246 blocks_collection: Collection<Document>,
247 blockchain_collection: Collection<Document>,
248 wallets_collection: Collection<Document>,
249) -> String {
250 let mut blockchain = load_blockchain(&blocks_collection, &blockchain_collection).await;
252
253 let transaction = get_transaction(tx_id.clone(), wallets_collection.clone()).await;
254 let data = serde_json::to_value(&transaction).expect("Failed to serialize transaction");
255 let mut transaction_data: Vec<Transaction> = Vec::new();
256 transaction_data.push(transaction.clone());
257
258 let block = Block::new(
260 blockchain.chain.len() as u64,
261 Utc::now().timestamp() as u64,
262 blockchain
263 .chain
264 .last()
265 .map_or(String::from("0"), |block| block.hash.clone()),
266 data,
267 String::from("Miner1"),
268 transaction_data,
269 );
270
271 blockchain.chain.push(block.clone());
273
274 let mut index_to_remove = 0;
276 for mp in blockchain.mempool.clone() {
277 if mp.tx_id == tx_id {
278 blockchain.mempool.remove(index_to_remove);
279 break;
280 }
281 index_to_remove += 1;
282 }
283
284 let mut sender_wallet =
286 mongo_utils::load_wallet(wallets_collection.clone(), transaction.sender.as_str())
287 .await
288 .unwrap();
289 let mut receiver_wallet =
290 mongo_utils::load_wallet(wallets_collection.clone(), transaction.receiver.as_str())
291 .await
292 .unwrap();
293
294 sender_wallet.transfer_txn(tx_id);
296
297 receiver_wallet.add_transaction(transaction.clone());
299 receiver_wallet.update_balance(transaction.amount);
300
301 let result = save_to_db(
303 &mut blockchain,
304 &mut sender_wallet,
305 &mut receiver_wallet,
306 blocks_collection,
307 wallets_collection,
308 blockchain_collection,
309 )
310 .await;
311
312 println!("Save db = {:?}", result);
313
314 let response = json!({
315 "message": "Listing New Block...",
316 "block": block.clone()
317 });
318
319 serde_json::to_string_pretty(&response).expect("Failed to serialize response to JSON")
321}
322
323pub async fn reset_sample_data(
327 blocks_collection: &Collection<Document>,
328 wallets_collection: &Collection<Document>,
329 blockchain_collection: &Collection<Document>,
330) -> Result<(), Box<dyn Error>> {
331 let _ = mongo_utils::reset_sample_data(
332 blockchain_collection.clone(),
333 blocks_collection.clone(),
334 wallets_collection.clone(),
335 )
336 .await;
337
338 Ok(())
339}
340
341async fn validate_txn(
345 blockchain: &Blockchain,
346 wallet1: &mut Wallet,
347 amount: f64,
348 fee: f64,
349) -> (i8, String) {
350 let mut msg = String::from("All transaction checks OK");
351 let mut is_valid = 0;
352 let txn_amount = amount + fee;
353
354 if wallet1.get_balance() < txn_amount {
355 msg = String::from("Transaction failed: Insufficient balance\n");
356 is_valid = -1;
357 }
358
359 if blockchain.chain.len() >= 10 {
360 msg = String::from(
361 "Due to system constraints no more than 10 blocks allowed\nPlease reset data.",
362 );
363 is_valid = -1;
364 }
365
366 if blockchain.mempool.len() >= 6 {
367 msg = String::from(
368 "Due to system constraints no more than 10 blocks allowed\nPlease reset data.",
369 );
370 is_valid = -1;
371 }
372
373 if wallet1.get_balance() < txn_amount {
374 msg = String::from("Insufficient balance");
375 is_valid = -1;
376 }
377
378 if txn_amount <= 0.0 || txn_amount.is_nan() || amount <= 0.0 || fee <= 0.0 {
379 msg = String::from("Not a valid amount.");
380 is_valid = -1;
381 }
382
383 (is_valid, msg)
384}
385
386async fn mine_transactions(
390 data: &str,
391 previous_hash: &str,
392 difficulty: usize,
393) -> Result<(u64, String, String), Box<dyn Error>> {
394 let (nonce, hash) = crate::mining::mine_block(data, previous_hash, difficulty, None);
395 let result = (
396 nonce,
397 hash.clone(),
398 format!("\nMining completed!\nNonce: {:>2}, Hash: {}", nonce, hash),
399 );
400
401 Ok(result)
402}
403
404async fn save_to_db(
408 blockchain: &mut Blockchain,
409 wallet1: &mut Wallet,
410 wallet2: &mut Wallet,
411 blocks_collection: Collection<Document>,
412 wallets_collection: Collection<Document>,
413 blockchain_collection: Collection<Document>,
414) -> Result<(), Box<dyn Error>> {
415 mongo_utils::save_blockchain(blockchain, blocks_collection, blockchain_collection).await?;
417 mongo_utils::save_wallet(wallet1.clone(), wallets_collection.clone()).await?;
418 mongo_utils::save_wallet(wallet2.clone(), wallets_collection).await?;
419
420 Ok(())
421}