library_blockchain/
blockchain.rs

1
2
3use serde::Serialize;
4
5use super::*;
6use crate::transaction::Put;
7
8use crate::transaction::IO;
9use std::collections::HashSet;
10
11
12
13/// Collection of related blocks as the same as linked lists
14/// In this program logic of POW algorithm have used.
15#[derive(Debug,Serialize)]
16pub struct Blockchain  {
17    pub blocks: Vec<Block >,
18    unspent_outputs: HashSet<Hash>,
19}
20
21impl  Default for Blockchain  {
22    fn default() -> Self {
23        Blockchain {
24            blocks: vec![],
25            unspent_outputs: HashSet::new(),
26        }
27    }
28}
29impl  Blockchain  {
30    pub fn new() -> Self {
31        Blockchain {
32            blocks: vec![],
33            unspent_outputs: HashSet::new(),
34        }
35    }
36
37    /// In the update_with_block() checking for (+)Overspending, (+)Double Spending, (-)Impersonate.
38    /// Define BlockValidationError for violation of the rules POW
39    /// Call input and output hash function  
40    /// Retain unspent_outputs of the Blockchain
41    pub fn blockchain_update_with_block(&mut self, block: Block ) -> Result<&Vec<Block>, CustomError> {
42        let i = &self.blocks.len();
43
44        if block.index != *i as u32 {
45            return Err(CustomError::BlockValidation(
46                BlockValidationError::MismatchedIndex,
47            ));
48            
49        } else if !block::blockchain_check_difficulty(&block.hash(), block.difficulty) {
50            return Err(CustomError::BlockValidation(
51                BlockValidationError::InvalidHash,
52            ));
53        } else if *i != 0 {
54            // Not genesis block
55            let prev_block = &self.blocks[i - 1];
56            if block.timestamp <= prev_block.timestamp {
57                return Err(CustomError::BlockValidation(
58                    BlockValidationError::AchronologicalTimestamp,
59                ));
60            } else if block.prev_block_hash != prev_block.hash {
61                return Err(CustomError::BlockValidation(
62                    BlockValidationError::MismatchedPreviousHash,
63                ));
64            }
65        } else {
66            // Genesis block
67            if block.prev_block_hash != vec![0; 32] {
68                return Err(CustomError::BlockValidation(
69                    BlockValidationError::InvalidGenesisBlockFormat,
70                ));
71            }
72        }
73
74        if let Some((coinbase, option_transactions)) = block.transactions.split_first() {
75            if !coinbase.is_coinbase() {
76                return Err(CustomError::BlockValidation(
77                    BlockValidationError::InvalidCoinbaseTransaction,
78                ));
79            }
80
81            let mut block_spent: HashSet<Hash> = HashSet::new();
82            let mut block_created: HashSet<Hash> = HashSet::new();
83            let mut total_fee = 0;
84          
85            for transaction in option_transactions {  
86                let input_hashes = transaction.returns_closure_io_hash(&IO::Input);
87                let output_hashes = transaction.returns_closure_io_hash(&IO::Output);
88
89                // info!("---------------------------\n");
90                // info!("input_hashes {:?}\n",input_hashes());
91                // info!("output_hashes {:?}\n",output_hashes());
92                // info!("unspent_outputs {:?}\n",&self.unspent_outputs);
93                // info!("block_spent {:?}\n",&block_spent);
94                // info!("---------------------------\n");
95                                             
96                if !(&input_hashes() - &self.unspent_outputs).is_empty()
97                    || !(&input_hashes() & &block_spent).is_empty()
98                {
99                    if *i==0 {                                
100                        //info!("input_hashes - unspent_outputs={}\n",(!(&input_hashes() - &self.unspent_outputs).is_empty()).to_string());
101                        //info!("input_hashes & block_spent={}\n",(!(&input_hashes() & &block_spent).is_empty()).to_string());
102                    
103                        return Err(CustomError::BlockValidation(
104                            BlockValidationError::InvalidInput
105                        ));
106                    }
107                }
108
109                let input_value = transaction.returns_closure_io(&IO::Input);
110                let output_value = transaction.returns_closure_io(&IO::Output);
111
112                if &output_value() > &input_value() {
113                    return Err(CustomError::BlockValidation(
114                        BlockValidationError::InsufficientInputValue,
115                    ));
116                }
117
118                let fee = &input_value() - &output_value();
119
120                total_fee += fee;
121
122                block_spent.extend(input_hashes());
123                block_created.extend(output_hashes());
124            }
125
126            let coinbase_output_value = coinbase.returns_closure_io(&IO::Output);
127
128            if coinbase_output_value() < total_fee {
129                return Err(CustomError::BlockValidation(
130                    BlockValidationError::InvalidCoinbaseTransactionFee,
131                ));
132            } else {
133                let coinbase_output_hashes = coinbase.returns_closure_io_hash(&IO::Output);
134                block_created.extend(coinbase_output_hashes());
135            }
136
137            self.unspent_outputs
138                .retain(|output| !block_spent.contains(output));
139            self.unspent_outputs.extend(block_created);
140        }
141        println!("**BlOcKcHaIn SiGnAls:**\n{:?}\n", &block);
142        let _ = &self.blocks.push(block);
143
144        Ok(&self.blocks)
145    }
146}