├── .gitignore ├── LICENSE ├── OLD_node.py ├── README.md ├── block.py ├── blockchain.py ├── check.py ├── node.py ├── transaction.py ├── utility ├── hash_util.py ├── printable.py └── verification.py └── wallet.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Aman Agarwal 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /OLD_node.py: -------------------------------------------------------------------------------- 1 | from utility.verification import Verification 2 | from blockchain import Blockchain 3 | from uuid import uuid4 4 | from wallet import Wallet 5 | class Node: 6 | 7 | def __init__(self): 8 | self.wallet = Wallet() 9 | self.wallet.create_keys() 10 | self.blockchain = Blockchain(self.wallet.public_key) 11 | 12 | def get_transaction_value(self): 13 | tx_recipent = input('Enter The Name Of Recipent: ') 14 | tx_amount = float(input('Enter The Amount Of Transaction: ')) 15 | 16 | return tx_recipent,tx_amount 17 | 18 | 19 | def get_user_choice(self): 20 | return input("Your Choice: ") 21 | 22 | def print_blockchain_elements(self): 23 | for block in self.blockchain.chain: 24 | print('Loading......') 25 | print(block) 26 | 27 | else: 28 | print('--'*10) 29 | 30 | 31 | def listen_for_input(self): 32 | qu = True 33 | 34 | 35 | while qu: 36 | print('Please Choose') 37 | print('1: Add a new transaction Value') 38 | print('2: Mine The Blockchain') 39 | print('3: Print The Blockchain') 40 | print('4: Check Transaction Validity') 41 | print('5: Create Wallet') 42 | print('6: Load Wallet') 43 | print('7: Save Keys') 44 | print('q: Quit') 45 | user_in = self.get_user_choice() 46 | 47 | if user_in=='1': 48 | tx_data = self.get_transaction_value() 49 | recipent,amount=tx_data 50 | 51 | signature = self.wallet.sign_transaction(self.wallet.public_key,recipent,amount) 52 | if self.blockchain.add_transaction(recipent,self.wallet.public_key,signature,amount=amount): 53 | print('Added Transaction') 54 | 55 | else: 56 | print('Transaction Failed') 57 | 58 | print(self.blockchain.get_open_transactions()) 59 | 60 | elif user_in == '2': 61 | if not self.blockchain.mine_block(): 62 | print('Mining Failed! Have U got Wallet??') 63 | elif user_in == '3': 64 | self.print_blockchain_elements() 65 | 66 | elif user_in == '4': 67 | if Verification.verify_transactions(self.blockchain.get_open_transactions(),self.blockchain.get_balance): 68 | print('All Transactions Are Valid') 69 | else: 70 | print('There Are Invalid Transactions') 71 | elif user_in=='5': 72 | self.wallet.create_keys() 73 | self.blockchain = Blockchain(self.wallet.public_key) 74 | elif user_in=='6': 75 | self.wallet.load_keys() 76 | self.blockchain = Blockchain(self.wallet.public_key) 77 | elif user_in=='7': 78 | self.wallet.save_keys() 79 | elif user_in =='q': 80 | qu = False 81 | else: 82 | print('Input is INVALID') 83 | if not Verification.verify_chain(self.blockchain.chain): 84 | self.print_blockchain_elements() 85 | print("Invalid Blockchain") 86 | break 87 | 88 | print('Balance of {}:{:4.2f}'.format(self.wallet.public_key,self.blockchain.get_balance())) 89 | else: 90 | print('User Left!!') 91 | print('Done!!!') 92 | 93 | 94 | if __name__ == '__main__': 95 | node = Node() 96 | node.listen_for_input() 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Blockchain-In-Python 2 | 3 | Implementation Of BlockChain In Python From Scratch With Your Own CrytoCurrency Like BITCOIN or Something 4 | 5 | Build With The Help Of Python And Flask 6 | 7 | ## Dependencies: 8 | 9 | - Python 3.x 10 | - Flask 11 | - Flask_cors 12 | - [Pycrypto](https://pypi.org/project/pycrypto/) 13 | - [Binascii](http://flask.pocoo.org/) 14 | - [Anaconda](https://docs.anaconda.com/anaconda/) 15 | 16 | Tip:Importing Classes From Different Scripts and Importing Methods Can Be An Tedious Job So Essentially Use A Code Editor Like VS Code Or Atom Something Like That 17 | 18 | - Extras: 19 | 20 | - Working With Flask And Understanding Restfull API During Reading `node.py` can Be Boring So Essentially Use Terminal Or any API Service Something Like Postman 21 | 22 | - Both Network UI and Node UI Both Are in `ui` Folder And Named respectively as `node.html` and `network.html`. 23 | 24 | 25 | ## Procedure: 26 | 27 | - Clone this repository `git clone https://github.com/Aman-py/Blockchain-In-Python` 28 | - After Cloning Repository Just Simply Go In the Folder 29 | 30 | Example 31 | ```bash 32 | $ cd .../Blockchain-In-Python/ 33 | $ python3 node.py 34 | ``` 35 | - After Running This U can Simply Open Your Browser And Run Url `http://localhost:5000/#` 36 | 37 | ## Result: 38 | 39 | 40 | ### Block UI 41 | 42 | 43 | ![block1](https://user-images.githubusercontent.com/29687692/45088910-1a215380-b128-11e8-9adb-28001058a2ce.png) 44 | 45 | ### Open Transaction UI 46 | 47 | 48 | ![transaction](https://user-images.githubusercontent.com/29687692/45090582-5e632280-b12d-11e8-81f8-431959939033.png) 49 | 50 | ### Nodes 51 | 52 | 53 | ![nodes](https://user-images.githubusercontent.com/29687692/45089224-1f32d280-b129-11e8-99be-bb97e026a25e.png) 54 | 55 | 56 | 57 | ##### Thanks to Maximilian Schwarzmüller 58 | -------------------------------------------------------------------------------- /block.py: -------------------------------------------------------------------------------- 1 | from time import time 2 | 3 | from utility.printable import Printable 4 | 5 | class Block(Printable): 6 | 7 | def __init__(self,index,previous_hash,transactions,proof,time=time()): 8 | self.previous_hash = previous_hash 9 | self.index=index 10 | self.transactions=transactions 11 | self.proof = proof 12 | self.timestamp = time 13 | 14 | -------------------------------------------------------------------------------- /blockchain.py: -------------------------------------------------------------------------------- 1 | # Initalizing our blockchain list 2 | from functools import reduce 3 | import hashlib as hl 4 | import json 5 | from collections import OrderedDict 6 | import json 7 | 8 | from utility.hash_util import hash_block 9 | from block import Block 10 | from transaction import Transaction 11 | from utility.verification import Verification 12 | from wallet import Wallet 13 | MINING_Reward = 10 14 | 15 | 16 | 17 | class Blockchain: 18 | def __init__(self,hosting_node): 19 | genesis_block = Block(0,'',[],100,0) 20 | self.chain = [genesis_block] 21 | self.__open_transaction = [] 22 | self.hosting_node = hosting_node 23 | self.__peer_nodes = set() 24 | self.load_data() 25 | 26 | @property 27 | def chain(self): 28 | return self.__chain[:] 29 | 30 | # The setter for the chain property 31 | @chain.setter 32 | def chain(self, val): 33 | self.__chain = val 34 | 35 | def get_open_transactions(self): 36 | return self.__open_transaction[:] 37 | 38 | 39 | def save_data(self): 40 | try: 41 | with open('blockchain.txt',mode='w') as f: 42 | savable_chain = [block.__dict__ for block in [Block(blok.index,blok.previous_hash,[tx.__dict__ for tx in blok.transactions],blok.proof,blok.timestamp) for blok in self.__chain]] 43 | f.write(json.dumps(savable_chain)) 44 | f.write('\n') 45 | savable_tx = [tx.__dict__ for tx in self.__open_transaction] 46 | f.write(json.dumps(savable_tx)) 47 | f.write('\n') 48 | f.write(json.dumps(self.__peer_nodes)) 49 | except: 50 | print('Saving Failed') 51 | 52 | 53 | def load_data(self): 54 | try: 55 | with open('blockchain.txt',mode='r') as f: 56 | file_content = f.readlines() 57 | blockchain = json.loads(file_content[0][:-1]) 58 | updated_blockchain = [] 59 | for block in blockchain: 60 | converted_tx = [Transaction(tx['sender'],tx['recipent'],tx['signature'],tx['amount']) for tx in block['transactions']] 61 | updated_block = Block(block['index'],block['previous_hash'],converted_tx,block['proof'],block['timestamp']) 62 | updated_blockchain.append(updated_block) 63 | self.chain=updated_blockchain 64 | open_transaction = json.loads(file_content[1][:-1]) 65 | updated_transactions = [] 66 | for tx in open_transaction: 67 | updated_transaction = Transaction(tx['sender'],tx['recipent'],tx['signature'],tx['amount']) 68 | updated_transactions.append(updated_transaction) 69 | self.__open_transaction=updated_transactions 70 | peer_nodes = json.loads(file_content[2]) 71 | self.__peer_nodes = set(peer_nodes) 72 | except (IOError,IndexError): 73 | pass 74 | 75 | 76 | 77 | def proof_of_work(self): 78 | last_block = self.__chain[-1] 79 | last_hash = hash_block(last_block) 80 | proof = 0 81 | while not Verification.valid_proof(self.__open_transaction,last_hash,proof): 82 | proof+=1 83 | return proof 84 | 85 | def get_balance(self): 86 | if self.hosting_node == None: 87 | return None 88 | participant=self.hosting_node 89 | tx_sender = [[tx.amount for tx in block.transactions if tx.sender==participant]for block in self.__chain] 90 | open_tx_sender = [tx.amount for tx in self.__open_transaction if tx.sender==participant] 91 | tx_sender.append(open_tx_sender) 92 | print(tx_sender) 93 | amount_sent = reduce(lambda tx_sum, tx_amt:tx_sum+sum(tx_amt) if len(tx_amt) >0 else tx_sum+0,tx_sender,0) 94 | tx_recipent = [[tx.amount for tx in block.transactions if tx.recipent==participant]for block in self.__chain] 95 | amount_recieved = reduce(lambda tx_sum, tx_amt:tx_sum+sum(tx_amt) if len(tx_amt) >0 else tx_sum+0,tx_recipent,0) 96 | 97 | return amount_recieved-amount_sent 98 | 99 | def get_last_blockchain_value(self): 100 | '''Returns the last value of blockchain''' 101 | if len(self.__chain)<1: 102 | return None 103 | return self.__chain[-1] 104 | 105 | def add_transaction(self,recipent,sender,signature,amount=1.0): 106 | '''append new value to your blockchain''' 107 | if self.hosting_node==None: 108 | return False 109 | 110 | transaction = Transaction(sender,recipent,signature,amount) 111 | if Verification.verify_transaction(transaction,self.get_balance): 112 | self.__open_transaction.append(transaction) 113 | self.save_data() 114 | return True 115 | return False 116 | 117 | def mine_block(self): 118 | if self.hosting_node==None: 119 | return None 120 | last_block = self.__chain[-1] 121 | hashed_block = hash_block(last_block) 122 | proof = self.proof_of_work() 123 | reward_transaction = Transaction('MINING',self.hosting_node,'',MINING_Reward) 124 | copied_transaction=self.__open_transaction[:] 125 | for tx in copied_transaction: 126 | if not Wallet.verify_transaction(tx): 127 | return None 128 | copied_transaction.append(reward_transaction) 129 | block = Block(len(self.__chain),hashed_block,copied_transaction,proof) 130 | self.__chain.append(block) 131 | self.__open_transaction =[] 132 | self.save_data() 133 | return block 134 | 135 | def add_peer_node(self,node): 136 | 137 | self.__peer_nodes.add(node) 138 | self.save_data() 139 | 140 | def remove_peer_nodes(self,node): 141 | self.__peer_nodes.discard(node) 142 | self.save_data() 143 | 144 | def get_peer_nodes(self): 145 | return list(self.__peer_nodes) -------------------------------------------------------------------------------- /check.py: -------------------------------------------------------------------------------- 1 | from utility.verification import Verification 2 | from blockchain import Blockchain 3 | from uuid import uuid4 4 | from wallet import Wallet 5 | class Node: 6 | 7 | def __init__(self): 8 | self.wallet = Wallet() 9 | self.wallet.create_keys() 10 | self.blockchain = Blockchain(self.wallet.public_key) 11 | 12 | def get_transaction_value(self): 13 | tx_recipent = input('Enter The Name Of Recipent: ') 14 | tx_amount = float(input('Enter The Amount Of Transaction: ')) 15 | 16 | return tx_recipent,tx_amount 17 | 18 | 19 | def get_user_choice(self): 20 | return input("Your Choice: ") 21 | 22 | def print_blockchain_elements(self): 23 | for block in self.blockchain.chain: 24 | print('Loading......') 25 | print(block) 26 | 27 | else: 28 | print('--'*10) 29 | 30 | 31 | def listen_for_input(self): 32 | qu = True 33 | 34 | 35 | while qu: 36 | print('Please Choose') 37 | print('1: Add a new transaction Value') 38 | print('2: Mine The Blockchain') 39 | print('3: Print The Blockchain') 40 | print('4: Check Transaction Validity') 41 | print('5: Create Wallet') 42 | print('6: Load Wallet') 43 | print('7: Save Keys') 44 | print('q: Quit') 45 | user_in = self.get_user_choice() 46 | 47 | if user_in=='1': 48 | tx_data = self.get_transaction_value() 49 | recipent,amount=tx_data 50 | 51 | 52 | if self.blockchain.add_transaction(recipent,self.wallet.public_key,amount=amount): 53 | print('Added Transaction') 54 | 55 | else: 56 | print('Transaction Failed') 57 | 58 | print(self.blockchain.get_open_transactions()) 59 | 60 | elif user_in == '2': 61 | if not self.blockchain.mine_block(): 62 | print('Mining Failed! Have U got Wallet??') 63 | elif user_in == '3': 64 | self.print_blockchain_elements() 65 | 66 | elif user_in == '4': 67 | if Verification.verify_transactions(self.blockchain.get_open_transactions(),self.blockchain.get_balance): 68 | print('All Transactions Are Valid') 69 | else: 70 | print('There Are Invalid Transactions') 71 | elif user_in=='5': 72 | self.wallet.create_keys() 73 | self.blockchain = Blockchain(self.wallet.public_key) 74 | elif user_in=='6': 75 | self.wallet.load_keys() 76 | self.blockchain = Blockchain(self.wallet.public_key) 77 | elif user_in=='7': 78 | self.wallet.save_keys() 79 | elif user_in =='q': 80 | qu = False 81 | else: 82 | print('Input is INVALID') 83 | if not Verification.verify_chain(self.blockchain.chain): 84 | self.print_blockchain_elements() 85 | print("Invalid Blockchain") 86 | break 87 | 88 | print('Balance of {}:{:4.2f}'.format(self.wallet.public_key,self.blockchain.get_balance())) 89 | else: 90 | print('User Left!!') 91 | print('Done!!!') 92 | 93 | 94 | if __name__ == '__main__': 95 | node = Node() 96 | node.listen_for_input() 97 | 98 | 99 | 100 | # Initalizing our blockchain list 101 | from functools import reduce 102 | import hashlib as hl 103 | import json 104 | from collections import OrderedDict 105 | import json 106 | 107 | from utility.hash_util import hash_block 108 | from block import Block 109 | from transaction import Transaction 110 | from utility.verification import Verification 111 | MINING_Reward = 10 112 | 113 | 114 | 115 | class Blockchain: 116 | def __init__(self,hosting_node): 117 | genesis_block = Block(0,'',[],100,0) 118 | self.chain = [genesis_block] 119 | self.__open_transaction = [] 120 | self.load_data() 121 | self.hosting_node = hosting_node 122 | 123 | @property 124 | def chain(self): 125 | return self.__chain[:] 126 | 127 | # The setter for the chain property 128 | @chain.setter 129 | def chain(self, val): 130 | self.__chain = val 131 | 132 | def get_open_transactions(self): 133 | return self.__open_transaction[:] 134 | 135 | 136 | def save_data(self): 137 | try: 138 | with open('blockchain.txt',mode='w') as f: 139 | savable_chain = [block.__dict__ for block in [Block(blok.index,blok.previous_hash,[tx.__dict__ for tx in blok.transactions],blok.proof,blok.timestamp) for blok in self.__chain]] 140 | f.write(json.dumps(savable_chain)) 141 | f.write('\n') 142 | savable_tx = [tx.__dict__ for tx in self.__open_transaction] 143 | f.write(json.dumps(savable_tx)) 144 | except: 145 | print('Saving Failed') 146 | 147 | 148 | def load_data(self): 149 | try: 150 | with open('blockchain.txt',mode='r') as f: 151 | file_content = f.readlines() 152 | blockchain = json.loads(file_content[0][:-1]) 153 | updated_blockchain = [] 154 | for block in blockchain: 155 | converted_tx = [Transaction(tx['sender'],tx['recipent'],tx['amount']) for tx in block['transactions']] 156 | updated_block = Block(block['index'],block['previous_hash'],converted_tx,block['proof'],block['timestamp']) 157 | updated_blockchain.append(updated_block) 158 | self.chain=updated_blockchain 159 | open_transaction = json.loads(file_content[1]) 160 | updated_transactions = [] 161 | for tx in open_transaction: 162 | updated_transaction = Transaction(tx['sender'],tx['recipent'],tx['amount']) 163 | updated_transactions.append(updated_transaction) 164 | self.__open_transaction=updated_transactions 165 | except (IOError,IndexError): 166 | pass 167 | 168 | 169 | 170 | def proof_of_work(self): 171 | last_block = self.__chain[-1] 172 | last_hash = hash_block(last_block) 173 | proof = 0 174 | while not Verification.valid_proof(self.__open_transaction,last_hash,proof): 175 | proof+=1 176 | return proof 177 | 178 | def get_balance(self): 179 | participant=self.hosting_node 180 | tx_sender = [[tx.amount for tx in block.transactions if tx.sender==participant]for block in self.__chain] 181 | open_tx_sender = [tx.amount for tx in self.__open_transaction if tx.sender==participant] 182 | tx_sender.append(open_tx_sender) 183 | print(tx_sender) 184 | amount_sent = reduce(lambda tx_sum, tx_amt:tx_sum+sum(tx_amt) if len(tx_amt) >0 else tx_sum+0,tx_sender,0) 185 | tx_recipent = [[tx.amount for tx in block.transactions if tx.recipent==participant]for block in self.__chain] 186 | amount_recieved = reduce(lambda tx_sum, tx_amt:tx_sum+sum(tx_amt) if len(tx_amt) >0 else tx_sum+0,tx_recipent,0) 187 | 188 | return amount_recieved-amount_sent 189 | 190 | def get_last_blockchain_value(self): 191 | '''Returns the last value of blockchain''' 192 | if len(self.__chain)<1: 193 | return None 194 | return self.__chain[-1] 195 | 196 | def add_transaction(self,recipent,sender,amount=1.0): 197 | '''append new value to your blockchain''' 198 | if self.hosting_node==None: 199 | return False 200 | 201 | transaction = Transaction(sender,recipent,amount) 202 | if Verification.verify_transaction(transaction,self.get_balance): 203 | self.__open_transaction.append(transaction) 204 | self.save_data() 205 | return True 206 | return False 207 | 208 | def mine_block(self): 209 | if self.hosting_node==None: 210 | return False 211 | last_block = self.__chain[-1] 212 | hashed_block = hash_block(last_block) 213 | proof = self.proof_of_work() 214 | reward_transaction = Transaction('MINING',self.hosting_node,MINING_Reward) 215 | copied_transaction=self.__open_transaction[:] 216 | copied_transaction.append(reward_transaction) 217 | block = Block(len(self.__chain),hashed_block,copied_transaction,proof) 218 | self.__chain.append(block) 219 | self.__open_transaction =[] 220 | self.save_data() 221 | return True 222 | -------------------------------------------------------------------------------- /node.py: -------------------------------------------------------------------------------- 1 | from flask import Flask,jsonify,request,send_from_directory 2 | from flask_cors import CORS 3 | 4 | from wallet import Wallet 5 | from blockchain import Blockchain 6 | 7 | app = Flask(__name__) 8 | wallet = Wallet() 9 | blockchain = Blockchain(wallet.public_key) 10 | CORS(app) 11 | 12 | @app.route('/',methods=['GET']) 13 | def get_node_ui(): 14 | return send_from_directory('ui','node.html') 15 | 16 | @app.route('/network',methods=['GET']) 17 | def get_network_ui(): 18 | return send_from_directory('ui','network.html') 19 | 20 | @app.route('/wallet',methods=['POST']) 21 | def create_keys(): 22 | wallet.create_keys() 23 | if wallet.save_keys(): 24 | global blockchain 25 | blockchain = Blockchain(wallet.public_key) 26 | response = { 27 | 'public_key':wallet.public_key, 28 | 'private_key':wallet.private_key, 29 | 'funds':blockchain.get_balance() 30 | } 31 | return jsonify(response),201 32 | else: 33 | response={ 34 | 'message':'Saving The keys failed' 35 | } 36 | return jsonify(response),500 37 | @app.route('/wallet',methods=['GET']) 38 | def load_keys(): 39 | if wallet.load_keys(): 40 | global blockchain 41 | blockchain = Blockchain(wallet.public_key) 42 | response = { 43 | 'public_key':wallet.public_key, 44 | 'private_key':wallet.private_key, 45 | 'funds':blockchain.get_balance() 46 | } 47 | return jsonify(response),201 48 | else: 49 | response={ 50 | 'message':'Loading The keys failed' 51 | } 52 | return jsonify(response),500 53 | 54 | @app.route('/balance',methods=['GET']) 55 | def get_balance(): 56 | balance = blockchain.get_balance() 57 | if balance != None: 58 | response = { 59 | 'message':'Fatched Balance Sucessfully.', 60 | 'funds':balance 61 | } 62 | return jsonify(response),201 63 | else: 64 | response = { 65 | 'message':'Loading Balance Failed', 66 | 'wallet_set_up':wallet.public_key!=None 67 | } 68 | return jsonify(response),500 69 | 70 | @app.route('/transaction',methods=['POST']) 71 | def add_transaction(): 72 | if wallet.public_key == None: 73 | response={ 74 | 'message':'No Wallet set up' 75 | } 76 | return jsonify(response),400 77 | values =request.get_json() 78 | if not values: 79 | response = { 80 | 'message':'No Data Found' 81 | } 82 | return jsonify(response),400 83 | required_fields = ['recipent','amount'] 84 | if not all(fields in values for fields in required_fields): 85 | response={ 86 | 'message':'Required Data is Missing' 87 | } 88 | 89 | return jsonify(response),400 90 | recipent = values['recipent'] 91 | amount = values['amount'] 92 | signature = wallet.sign_transaction(wallet.public_key,recipent,amount) 93 | sucess=blockchain.add_transaction(recipent,wallet.public_key,signature,amount) 94 | if sucess: 95 | response={ 96 | 'message':'Sucessfully Added a transaction', 97 | 'transaction':{ 98 | 'sender':wallet.public_key, 99 | 'recipent':recipent, 100 | 'amount':amount, 101 | 'signature':signature 102 | }, 103 | 'funds':blockchain.get_balance() 104 | } 105 | return jsonify(response),201 106 | 107 | else: 108 | response={ 109 | 'message':'Creating a transaction failed' 110 | } 111 | return jsonify(response),500 112 | 113 | 114 | @app.route('/mine',methods=['POST']) 115 | def mine(): 116 | block =blockchain.mine_block() 117 | if block != None: 118 | dict_block = block.__dict__.copy() 119 | dict_block['transactions'] = [tx.__dict__ for tx in dict_block['transactions']] 120 | response = { 121 | 'message':'Block Added Sucessfully', 122 | 'Block':dict_block, 123 | 'funds':blockchain.get_balance() 124 | } 125 | return jsonify(response),201 126 | else: 127 | response = { 128 | 'message':'Adding Block Failed', 129 | 'wallet_set_up':wallet.public_key!=None 130 | } 131 | return jsonify(response),500 132 | @app.route('/transactions',methods=['GET']) 133 | def get_open_transaction(): 134 | transactions = blockchain.get_open_transactions() 135 | dict_transaction = [tx.__dict__ for tx in transactions] 136 | return jsonify(dict_transaction),200 137 | 138 | @app.route('/chain',methods=['GET']) 139 | def get_chain(): 140 | chain_snapshot = blockchain.chain 141 | dict_chain = [block.__dict__.copy() for block in chain_snapshot] 142 | for dict_block in dict_chain: 143 | dict_block['transactions']=[tx.__dict__ for tx in dict_block['transactions']] 144 | return jsonify(dict_chain),200 145 | 146 | 147 | @app.route('/node',methods=['POST']) 148 | def add_node(): 149 | values = request.get_json() 150 | if not values: 151 | response = { 152 | 'message':'No Data Attached' 153 | } 154 | return jsonify(response),400 155 | if 'node' not in values: 156 | response = { 157 | 'message':'No node Data found' 158 | } 159 | return jsonify(response),400 160 | node = values['node'] 161 | blockchain.add_peer_node(node) 162 | response = { 163 | 'message':'Node Added Sucessfully', 164 | 'all_nodes':blockchain.get_peer_nodes() 165 | } 166 | return jsonify(response),201 167 | 168 | 169 | @app.route('/node/',methods=['DELETE']) 170 | def remove_node(node_url): 171 | if node_url == '' or node_url == None: 172 | response = { 173 | 'message':'No node found' 174 | } 175 | return jsonify(response),400 176 | blockchain.remove_peer_nodes(node_url) 177 | response={ 178 | 'message':'Node Removed', 179 | 'all_nodes':blockchain.get_peer_nodes() 180 | } 181 | return jsonify(response),200 182 | 183 | @app.route('/nodes',methods=['GET']) 184 | def get_nodes(): 185 | nodes=blockchain.get_peer_nodes() 186 | response={ 187 | 'all_nodes':nodes 188 | } 189 | return jsonify(response),200 190 | 191 | if __name__ == '__main__': 192 | app.run(host='0.0.0.0',port=5000) -------------------------------------------------------------------------------- /transaction.py: -------------------------------------------------------------------------------- 1 | from collections import OrderedDict 2 | 3 | from utility.printable import Printable 4 | 5 | class Transaction(Printable): 6 | 7 | def __init__(self,sender,recipent,signature,amount): 8 | self.sender=sender 9 | self.recipent=recipent 10 | self.amount=amount 11 | self.signature = signature 12 | 13 | def to_ordered_dict(self): 14 | return OrderedDict([('sender',self.sender),('recipent',self.recipent),('amount',self.amount)]) 15 | -------------------------------------------------------------------------------- /utility/hash_util.py: -------------------------------------------------------------------------------- 1 | import hashlib as hl 2 | import json 3 | 4 | def hash_string_256(string): 5 | return hl.sha256(string).hexdigest() 6 | 7 | def hash_block(block): 8 | 9 | hashable_block = block.__dict__.copy() 10 | hashable_block['transactions'] = [tx.to_ordered_dict() for tx in hashable_block['transactions']] 11 | return hl.sha256(json.dumps(hashable_block,sort_keys=True).encode()).hexdigest() 12 | -------------------------------------------------------------------------------- /utility/printable.py: -------------------------------------------------------------------------------- 1 | class Printable: 2 | def __repr__(self): 3 | return str(self.__dict__) -------------------------------------------------------------------------------- /utility/verification.py: -------------------------------------------------------------------------------- 1 | from utility.hash_util import hash_block,hash_string_256 2 | from wallet import Wallet 3 | 4 | 5 | class Verification: 6 | @classmethod 7 | def verify_chain(cls,blockchain): 8 | 9 | for (index,block) in enumerate(blockchain): 10 | if index == 0: 11 | continue 12 | 13 | if block.previous_hash != hash_block(blockchain[index-1]): 14 | return False 15 | 16 | if not cls.valid_proof(block.transactions[:-1],block.previous_hash,block.proof): 17 | print('Proof Of Work is not Verified') 18 | return False 19 | return True 20 | 21 | 22 | @classmethod 23 | def verify_transactions(cls,open_transaction,get_balance): 24 | return all([cls.verify_transaction(tx,get_balance,False) for tx in open_transaction]) 25 | 26 | 27 | @staticmethod 28 | def verify_transaction(transaction,get_balance,check_funds = True): 29 | if check_funds: 30 | sender_balance = get_balance() 31 | return sender_balance >=transaction.amount and Wallet.verify_transaction(transaction) 32 | else: 33 | return Wallet.verify_transaction(transaction) 34 | 35 | @staticmethod 36 | def valid_proof(trasactions,last_hash,proof): 37 | guess = (str([tx.to_ordered_dict() for tx in trasactions])+str(last_hash)+str(proof)).encode() 38 | guess_hash = hash_string_256(guess) 39 | return guess_hash[0:2] == '00' 40 | 41 | -------------------------------------------------------------------------------- /wallet.py: -------------------------------------------------------------------------------- 1 | from Crypto.PublicKey import RSA 2 | import Crypto.Random 3 | from Crypto.Signature import PKCS1_v1_5 4 | from Crypto.Hash import SHA256 5 | import binascii 6 | 7 | class Wallet: 8 | def __init__(self): 9 | self.private_key = None 10 | self.public_key = None 11 | 12 | def create_keys(self): 13 | private_key, public_key = self.generate_keys() 14 | self.private_key = private_key 15 | self.public_key = public_key 16 | 17 | 18 | def save_keys(self): 19 | if self.public_key != None and self.private_key != None: 20 | try: 21 | with open('wallet.txt', mode='w') as f: 22 | f.write(self.public_key) 23 | f.write('\n') 24 | f.write(self.private_key) 25 | return True 26 | except (IOError, IndexError): 27 | print('Saving wallet failed...') 28 | return False 29 | def load_keys(self): 30 | try: 31 | with open('wallet.txt', mode='r') as f: 32 | keys = f.readlines() 33 | public_key = keys[0][:-1] 34 | private_key = keys[1] 35 | self.public_key = public_key 36 | self.private_key = private_key 37 | return True 38 | except (IOError, IndexError): 39 | print('Loading wallet failed...') 40 | return False 41 | def generate_keys(self): 42 | private_key = RSA.generate(1024,Crypto.Random.new().read) 43 | public_key = private_key.publickey() 44 | return (binascii.hexlify(private_key.exportKey(format="DER")).decode('ascii'),binascii.hexlify(public_key.exportKey(format="DER")).decode('ascii')) 45 | 46 | def sign_transaction(self,sender,recipent,amount): 47 | signer = PKCS1_v1_5.new(RSA.importKey(binascii.unhexlify(self.private_key))) 48 | h = SHA256.new((str(sender)+str(recipent)+str(amount)).encode('utf8')) 49 | signature = signer.sign(h) 50 | return binascii.hexlify(signature).decode('ascii') 51 | 52 | 53 | @staticmethod 54 | def verify_transaction(transaction): 55 | public_key = RSA.importKey(binascii.unhexlify(transaction.sender)) 56 | verifier = PKCS1_v1_5.new(public_key) 57 | h = SHA256.new((str(transaction.sender)+str(transaction.recipent)+str(transaction.amount)).encode('utf8')) 58 | return verifier.verify(h,binascii.unhexlify(transaction.signature)) --------------------------------------------------------------------------------