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}