blockchain/chain.rs
1use std::{collections::HashMap, fmt::Write, hash::BuildHasherDefault, iter};
2
3use derive_builder::Builder;
4use rand::Rng;
5use serde::{Deserialize, Serialize};
6use sha2::{Digest, Sha256};
7use twox_hash::XxHash64;
8
9use crate::{Block, BlockchainError, Transaction, Wallet};
10
11/// A map of transactions.
12pub type ChainTransactions = HashMap<String, Transaction, BuildHasherDefault<XxHash64>>;
13
14/// A map of wallets.
15pub type ChainWallets = HashMap<String, Wallet, BuildHasherDefault<XxHash64>>;
16
17/// Blockchain.
18#[derive(Clone, Debug, Default, Builder, Serialize, Deserialize)]
19pub struct Chain {
20 /// Chain of blocks.
21 pub chain: Vec<Block>,
22
23 /// List of transactions.
24 pub transactions: ChainTransactions,
25
26 /// Current difficulty level of the network.
27 pub difficulty: f64,
28
29 /// Blockchain genesis address.
30 pub address: String,
31
32 /// Block reward.
33 pub reward: f64,
34
35 /// Transaction fee.
36 pub fee: f64,
37
38 /// Map to associate wallets with their corresponding addresses and balances.
39 pub wallets: ChainWallets,
40}
41
42impl Chain {
43 /// Initialize a new blockchain with the specified parameters.
44 ///
45 /// # Arguments
46 /// - `difficulty`: The initial mining difficulty level of the network.
47 /// - `reward`: The initial block reward for miners.
48 /// - `fee`: The transaction fee.
49 ///
50 /// # Returns
51 /// New `Chain` instance with the given parameters and a genesis block.
52 pub fn new(difficulty: f64, reward: f64, fee: f64) -> Self {
53 let mut chain = Chain {
54 fee,
55 reward,
56 difficulty,
57 chain: vec![],
58 wallets: HashMap::default(),
59 transactions: HashMap::default(),
60 address: Chain::generate_address(42),
61 };
62
63 chain.generate_new_block();
64
65 chain
66 }
67
68 /// Get a list of current transactions in the blockchain.
69 ///
70 /// # Arguments
71 /// - `page`: The page number.
72 /// - `size`: The number of transactions per page.
73 ///
74 /// # Returns
75 /// A list of transactions for the specified page.
76 pub fn get_transactions(&self, page: usize, size: usize) -> ChainTransactions {
77 // Calculate the total number of pages
78 let total_transactions = self.transactions.len();
79 let total_pages = total_transactions.div_ceil(size);
80
81 // Return an empty vector if the page is greater than the total number of pages
82 if page > total_pages {
83 return HashMap::default();
84 }
85
86 // Calculate the start and end indices for the transactions of the current page
87 let start = page.saturating_sub(1) * size;
88 let end = start + size;
89
90 // Get the transactions for the current page
91 self.transactions
92 .iter()
93 .skip(start)
94 .take(end.min(total_transactions))
95 .map(|(k, v)| (k.to_owned(), v.to_owned()))
96 .collect()
97 }
98
99 /// Get a transaction by its identifier.
100 ///
101 /// # Arguments
102 /// - `hash`: The hash of the transaction to retrieve.
103 ///
104 /// # Returns
105 /// An option containing a reference to the transaction if found, or `None` if not found.
106 pub fn get_transaction(&self, hash: &str) -> Result<&Transaction, BlockchainError> {
107 match self.transactions.get(hash) {
108 Some(transaction) => Ok(transaction),
109 None => Err(BlockchainError::TransactionNotFound),
110 }
111 }
112
113 /// Add a new transaction to the blockchain.
114 ///
115 /// # Arguments
116 /// - `from`: The sender's address.
117 /// - `to`: The receiver's address.
118 /// - `amount`: The amount of the transaction.
119 ///
120 /// # Returns
121 /// `true` if the transaction is successfully added to the current transactions.
122 pub fn add_transaction(
123 &mut self,
124 from: String,
125 to: String,
126 amount: f64,
127 ) -> Result<(), BlockchainError> {
128 let total = amount * self.fee;
129
130 // Validate the transaction and create a new transaction if it is valid
131 let transaction = match self.validate_transaction(&from, &to, total) {
132 true => Transaction::new(from.to_owned(), to.to_owned(), self.fee, total),
133 false => return Err(BlockchainError::InvalidTransaction),
134 };
135
136 // Update sender's balance
137 match self.wallets.get_mut(&from) {
138 Some(wallet) => {
139 // Determine the wallet balance is sufficient for the transaction. If not, return false.
140 if wallet.balance < total {
141 return Err(BlockchainError::InsufficientFunds);
142 }
143
144 wallet.balance -= total;
145
146 // Add the transaction to the sender's transaction history
147 wallet.transaction_hashes.push(transaction.hash.to_owned());
148 }
149 None => return Err(BlockchainError::WalletNotFound),
150 };
151
152 // Update receiver's balance
153 match self.wallets.get_mut(&to) {
154 Some(wallet) => {
155 wallet.balance += amount;
156
157 // Add the transaction to the receiver's transaction history
158 wallet.transaction_hashes.push(transaction.hash.to_owned());
159 }
160 None => return Err(BlockchainError::WalletNotFound),
161 };
162
163 // Add the transaction to the current transactions
164 self.transactions
165 .insert(transaction.hash.to_owned(), transaction);
166
167 Ok(())
168 }
169
170 /// Validate a transaction.
171 ///
172 /// # Arguments
173 /// - `from`: The sender's address.
174 /// - `to`: The receiver's address.
175 /// - `amount`: The amount of the transaction.
176 ///
177 /// # Returns
178 /// `true` if the transaction is valid, `false` otherwise.
179 pub fn validate_transaction(&self, from: &str, to: &str, amount: f64) -> bool {
180 // Validate if the sender is not the root
181 if from == "Root" {
182 return false;
183 }
184
185 // Validate that sender and receiver addresses are different
186 if from == to {
187 return false;
188 }
189
190 // Validate if the amount is non-negative
191 if amount <= 0.0 {
192 return false;
193 }
194
195 // Validate if sender and receiver addresses are valid
196 let sender = match self.wallets.get(from) {
197 Some(wallet) => wallet,
198 None => return false,
199 };
200
201 // Validate if the receiver address is valid
202 if !self.wallets.contains_key(to) {
203 return false;
204 }
205
206 // Validate if sender can send the amount of the transaction
207 if sender.balance < amount {
208 return false;
209 }
210
211 true
212 }
213
214 /// Create a new wallet with a unique email and an initial balance.
215 ///
216 /// # Arguments
217 /// - `email`: The unique user email.
218 ///
219 /// # Returns
220 /// The newly created wallet address.
221 pub fn create_wallet(&mut self, email: &str) -> String {
222 let address = Chain::generate_address(42);
223 let wallet = Wallet::new(email, &address);
224
225 self.wallets.insert(address.to_string(), wallet);
226
227 address
228 }
229
230 /// Get a wallet's balance based on its address.
231 ///
232 /// # Arguments
233 /// - `address`: The unique wallet address.
234 ///
235 /// # Returns
236 /// The wallet balance.
237 pub fn get_wallet_balance(&self, address: &str) -> Option<f64> {
238 self.wallets.get(address).map(|wallet| wallet.balance)
239 }
240
241 /// Get a wallet's transaction history based on its address.
242 ///
243 /// # Arguments
244 /// - `address`: The unique wallet address.
245 /// - `page`: The page number.
246 /// - `size`: The number of transactions per page.
247 ///
248 /// # Returns
249 /// The wallet transaction history for the specified page.
250 pub fn get_wallet_transactions(
251 &self,
252 address: &str,
253 page: usize,
254 size: usize,
255 ) -> Option<Vec<Transaction>> {
256 match self.wallets.get(address) {
257 // Get the transaction history of the wallet
258 Some(wallet) => {
259 let mut result = vec![];
260
261 // Calculate the total number of pages
262 let total_pages = self.transactions.len().div_ceil(size);
263
264 // Return an empty vector if the page is greater than the total number of pages
265 if page > total_pages {
266 return Some(result);
267 }
268
269 // Calculate the start and end indices for the transactions of the current page
270 let start = page.saturating_sub(1) * size;
271 let end = start + size;
272 let hashes = &wallet.transaction_hashes;
273
274 for tx in hashes[start..end.min(hashes.len())].iter() {
275 match self.get_transaction(tx) {
276 Ok(transaction) => result.push(transaction.to_owned()),
277 Err(_) => continue,
278 }
279 }
280
281 Some(result)
282 }
283 // Return None if the wallet is not found
284 None => None,
285 }
286 }
287
288 /// Get the hash of the last block in the blockchain.
289 ///
290 /// # Returns
291 /// The hash of the last block in the blockchain as a string.
292 pub fn get_last_hash(&self) -> String {
293 let block = match self.chain.last() {
294 Some(block) => block,
295 None => return String::from_utf8(vec![48; 64]).unwrap(),
296 };
297
298 Chain::hash(&block.header)
299 }
300
301 /// Update the mining difficulty of the blockchain.
302 ///
303 /// # Arguments
304 /// - `difficulty`: The new mining difficulty level.
305 pub fn update_difficulty(&mut self, difficulty: f64) {
306 self.difficulty = difficulty;
307 }
308
309 /// Update the block reward.
310 ///
311 /// # Arguments
312 /// - `reward`: The new block reward value.
313 pub fn update_reward(&mut self, reward: f64) {
314 self.reward = reward;
315 }
316
317 /// Update the transaction fee.
318 ///
319 /// # Arguments
320 /// - `fee`: The new transaction fee value.
321 pub fn update_fee(&mut self, fee: f64) {
322 self.fee = fee;
323 }
324
325 /// Generate a new block and append it to the blockchain.
326 ///
327 /// # Returns
328 /// `true` if a new block is successfully generated and added to the blockchain.
329 pub fn generate_new_block(&mut self) -> bool {
330 // Create a new block
331 let mut block = Block::new(self.get_last_hash(), self.difficulty);
332
333 // Create a reward transaction
334 let transaction = Transaction::new(
335 "Root".to_string(),
336 self.address.to_string(),
337 self.fee,
338 self.reward,
339 );
340
341 // Add the reward transaction to the block
342 block
343 .transactions
344 .insert(transaction.hash.to_owned(), transaction);
345
346 // Update the block count and the Merkle root hash
347 block.header.merkle = Chain::get_merkle(block.transactions.clone());
348
349 // Perform the proof-of-work process
350 Block::proof_of_work(&mut block.header);
351
352 // Add the block to the blockchain
353 self.chain.push(block);
354
355 true
356 }
357
358 /// Calculate the Merkle root hash for a list of transactions.
359 ///
360 /// # Arguments
361 /// - `transactions`: A vector of transactions for which the Merkle root hash is calculated.
362 ///
363 /// # Returns
364 /// The Merkle root hash as a string.
365 pub fn get_merkle(transactions: ChainTransactions) -> String {
366 let mut merkle = vec![];
367
368 for transaction in transactions.values() {
369 let hash = Chain::hash(transaction);
370 merkle.push(hash);
371 }
372
373 if merkle.len() % 2 == 1 {
374 let last = merkle.last().cloned().unwrap();
375 merkle.push(last);
376 }
377
378 while merkle.len() > 1 {
379 let mut h1 = merkle.remove(0);
380 let h2 = merkle.remove(0);
381
382 h1.push_str(&h2);
383
384 let nh = Chain::hash(&h1);
385 merkle.push(nh);
386 }
387
388 merkle.pop().unwrap()
389 }
390
391 /// Calculate the SHA-256 hash of a serializable item.
392 ///
393 /// # Arguments
394 /// - `item`: A serializable item to be hashed.
395 ///
396 /// # Returns
397 /// The SHA-256 hash of the item as a string.
398 pub fn hash<T: serde::Serialize>(item: &T) -> String {
399 let input = serde_json::to_string(&item).unwrap();
400 let mut hasher = Sha256::new();
401 hasher.update(input.as_bytes());
402 let res = hasher.finalize();
403 let vec_res = res.to_vec();
404
405 let mut result = String::new();
406
407 for b in vec_res.as_slice() {
408 write!(&mut result, "{:x}", b).expect("Unable to write");
409 }
410
411 result
412 }
413
414 /// Generates a random alphanumeric string of a specified length.
415 ///
416 /// # Arguments
417 /// - `length`: The length of the generated string.
418 ///
419 /// # Returns
420 /// A `String` containing the generated alphanumeric string.
421 pub fn generate_address(length: usize) -> String {
422 let mut rng = rand::thread_rng();
423
424 let address: String = iter::repeat(())
425 .map(|()| rng.sample(rand::distributions::Alphanumeric) as char)
426 .take(length)
427 .collect();
428
429 address
430 }
431}
432
433#[cfg(test)]
434mod tests {
435 use super::*;
436
437 #[test]
438 fn test_generate_address() {
439 let result = Chain::generate_address(42);
440
441 assert_eq!(result.len(), 42);
442 }
443}