├── Block_Chain.py └── README.md /Block_Chain.py: -------------------------------------------------------------------------------- 1 | import hashlib 2 | import json 3 | from time import time 4 | from urllib.parse import urlparse 5 | from uuid import uuid4 6 | 7 | import requests 8 | from flask import Flask, jsonify, request 9 | 10 | 11 | class Blockchain: 12 | def __init__(self): 13 | self.current_transactions = [] 14 | self.chain = [] 15 | self.nodes = set() 16 | 17 | # Create the genesis block 18 | self.new_block(previous_hash='1', proof=100) 19 | 20 | def register_node(self, address): 21 | """ 22 | Add a new node to the list of nodes 23 | :param address: Address of node. Eg. 'http://192.168.0.5:5000' 24 | """ 25 | 26 | parsed_url = urlparse(address) 27 | self.nodes.add(parsed_url.netloc) 28 | 29 | def valid_chain(self, chain): 30 | """ 31 | Determine if a given blockchain is valid 32 | :param chain: A blockchain 33 | :return: True if valid, False if not 34 | """ 35 | 36 | last_block = chain[0] 37 | current_index = 1 38 | 39 | while current_index < len(chain): 40 | block = chain[current_index] 41 | print(f'{last_block}') 42 | print(f'{block}') 43 | print("\n-----------\n") 44 | # Check that the hash of the block is correct 45 | if block['previous_hash'] != self.hash(last_block): 46 | return False 47 | 48 | # Check that the Proof of Work is correct 49 | if not self.valid_proof(last_block['proof'], block['proof']): 50 | return False 51 | 52 | last_block = block 53 | current_index += 1 54 | 55 | return True 56 | 57 | def resolve_conflicts(self): 58 | """ 59 | This is our consensus algorithm, it resolves conflicts 60 | by replacing our chain with the longest one in the network. 61 | :return: True if our chain was replaced, False if not 62 | """ 63 | 64 | neighbours = self.nodes 65 | new_chain = None 66 | 67 | # We're only looking for chains longer than ours 68 | max_length = len(self.chain) 69 | 70 | # Grab and verify the chains from all the nodes in our network 71 | for node in neighbours: 72 | response = requests.get(f'http://{node}/chain') 73 | 74 | if response.status_code == 200: 75 | length = response.json()['length'] 76 | chain = response.json()['chain'] 77 | 78 | # Check if the length is longer and the chain is valid 79 | if length > max_length and self.valid_chain(chain): 80 | max_length = length 81 | new_chain = chain 82 | 83 | # Replace our chain if we discovered a new, valid chain longer than ours 84 | if new_chain: 85 | self.chain = new_chain 86 | return True 87 | 88 | return False 89 | 90 | def new_block(self, proof, previous_hash): 91 | """ 92 | Create a new Block in the Blockchain 93 | :param proof: The proof given by the Proof of Work algorithm 94 | :param previous_hash: Hash of previous Block 95 | :return: New Block 96 | """ 97 | 98 | block = { 99 | 'index': len(self.chain) + 1, 100 | 'timestamp': time(), 101 | 'transactions': self.current_transactions, 102 | 'proof': proof, 103 | 'previous_hash': previous_hash or self.hash(self.chain[-1]), 104 | } 105 | 106 | # Reset the current list of transactions 107 | self.current_transactions = [] 108 | 109 | self.chain.append(block) 110 | return block 111 | 112 | def new_transaction(self, sender, recipient, amount): 113 | """ 114 | Creates a new transaction to go into the next mined Block 115 | :param sender: Address of the Sender 116 | :param recipient: Address of the Recipient 117 | :param amount: Amount 118 | :return: The index of the Block that will hold this transaction 119 | """ 120 | self.current_transactions.append({ 121 | 'sender': sender, 122 | 'recipient': recipient, 123 | 'amount': amount, 124 | }) 125 | 126 | return self.last_block['index'] + 1 127 | 128 | @property 129 | def last_block(self): 130 | return self.chain[-1] 131 | 132 | @staticmethod 133 | def hash(block): 134 | """ 135 | Creates a SHA-256 hash of a Block 136 | :param block: Block 137 | """ 138 | 139 | # We must make sure that the Dictionary is Ordered, or we'll have inconsistent hashes 140 | block_string = json.dumps(block, sort_keys=True).encode() 141 | return hashlib.sha256(block_string).hexdigest() 142 | 143 | def proof_of_work(self, last_proof): 144 | """ 145 | Simple Proof of Work Algorithm: 146 | - Find a number p' such that hash(pp') contains leading 4 zeroes, where p is the previous p' 147 | - p is the previous proof, and p' is the new proof 148 | """ 149 | 150 | proof = 0 151 | while self.valid_proof(last_proof, proof) is False: 152 | proof += 1 153 | 154 | return proof 155 | 156 | @staticmethod 157 | def valid_proof(last_proof, proof): 158 | """ 159 | Validates the Proof 160 | :param last_proof: Previous Proof 161 | :param proof: Current Proof 162 | :return: True if correct, False if not. 163 | """ 164 | 165 | guess = f'{last_proof}{proof}'.encode() 166 | guess_hash = hashlib.sha256(guess).hexdigest() 167 | return guess_hash[:4] == "0000" 168 | 169 | 170 | # Instantiate the Node 171 | app = Flask(__name__) 172 | 173 | # Generate a globally unique address for this node 174 | node_identifier = str(uuid4()).replace('-', '') 175 | 176 | # Instantiate the Blockchain 177 | blockchain = Blockchain() 178 | 179 | 180 | @app.route('/mine', methods=['GET']) 181 | def mine(): 182 | # We run the proof of work algorithm to get the next proof... 183 | last_block = blockchain.last_block 184 | last_proof = last_block['proof'] 185 | proof = blockchain.proof_of_work(last_proof) 186 | 187 | # We must receive a reward for finding the proof. 188 | # The sender is "0" to signify that this node has mined a new coin. 189 | blockchain.new_transaction( 190 | sender="0", 191 | recipient=node_identifier, 192 | amount=1, 193 | ) 194 | 195 | # Forge the new Block by adding it to the chain 196 | previous_hash = blockchain.hash(last_block) 197 | block = blockchain.new_block(proof, previous_hash) 198 | 199 | response = { 200 | 'message': "New Block Forged", 201 | 'index': block['index'], 202 | 'transactions': block['transactions'], 203 | 'proof': block['proof'], 204 | 'previous_hash': block['previous_hash'], 205 | } 206 | return jsonify(response), 200 207 | 208 | 209 | @app.route('/transactions/new', methods=['POST']) 210 | def new_transaction(): 211 | values = request.get_json() 212 | 213 | # Check that the required fields are in the POST'ed data 214 | required = ['sender', 'recipient', 'amount'] 215 | if not all(k in values for k in required): 216 | return 'Missing values', 400 217 | 218 | # Create a new Transaction 219 | index = blockchain.new_transaction(values['sender'], values['recipient'], values['amount']) 220 | 221 | response = {'message': f'Transaction will be added to Block {index}'} 222 | return jsonify(response), 201 223 | 224 | 225 | @app.route('/chain', methods=['GET']) 226 | def full_chain(): 227 | response = { 228 | 'chain': blockchain.chain, 229 | 'length': len(blockchain.chain), 230 | } 231 | return jsonify(response), 200 232 | 233 | 234 | @app.route('/nodes/register', methods=['POST']) 235 | def register_nodes(): 236 | values = request.get_json() 237 | 238 | nodes = values.get('nodes') 239 | if nodes is None: 240 | return "Error: Please supply a valid list of nodes", 400 241 | 242 | for node in nodes: 243 | blockchain.register_node(node) 244 | 245 | response = { 246 | 'message': 'New nodes have been added', 247 | 'total_nodes': list(blockchain.nodes), 248 | } 249 | return jsonify(response), 201 250 | 251 | 252 | @app.route('/nodes/resolve', methods=['GET']) 253 | def consensus(): 254 | replaced = blockchain.resolve_conflicts() 255 | 256 | if replaced: 257 | response = { 258 | 'message': 'Our chain was replaced', 259 | 'new_chain': blockchain.chain 260 | } 261 | else: 262 | response = { 263 | 'message': 'Our chain is authoritative', 264 | 'chain': blockchain.chain 265 | } 266 | 267 | return jsonify(response), 200 268 | 269 | 270 | if __name__ == '__main__': 271 | from argparse import ArgumentParser 272 | 273 | parser = ArgumentParser() 274 | parser.add_argument('-p', '--port', default=5000, type=int, help='port to listen on') 275 | args = parser.parse_args() 276 | port = args.port 277 | 278 | app.run(host='0.0.0.0', port=port) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Block-Chain-Example 2 | Simple example of blockchain using flask in python. 3 | 4 | ## Prerequisites 5 | - Python 3.0+ 6 | - Flask and Requests 7 | ``` 8 | # for install 9 | pip install Flask==0.12.2 requests==2.18.4 10 | ``` 11 | ### Major steps 12 | 1. Implementing a basic proof of work 13 | 2. Making our blockchain an API endpoint 14 | 3. Creating a miner for your blockchain 15 | 4. Interacting with the blockchain 16 | 17 | 18 | ### Step 1 - Implementing a basic proof of work 19 | Understanding Proof of Work 20 | 21 | A Proof of Work algorithm (PoW) is how new Blocks are created or mined on the blockchain. 22 | 23 | The goal of PoW is to discover a number which solves a problem. The number must be difficult to find but easy to verify for anyone on the network. The number is made of cryptographic signatures, meaning that if the wrong number is sent somewhere it will be denied access. Thus the PoW algorithm means you can now send money without having to trust anybody or any organisation, as the blockchain only cares about the cryptographic signatures. This is the core idea behind Proof of Work. 24 | 25 |

26 | 27 |

str: 41 | """ 42 | Creates a SHA-256 hash of a Block 43 | :param block: Block 44 | """ 45 | 46 | # We must make sure that the Dictionary is Ordered, or we'll have inconsistent hashes 47 | block_string = json.dumps(block, sort_keys=True).encode() 48 | return hashlib.sha256(block_string).hexdigest() 49 | 50 | def proof_of_work(self, last_proof: int) -> int: 51 | """ 52 | Simple Proof of Work Algorithm: 53 | - Find a number 0' such that hash(00') contains leading 4 zeroes, where 0 is the previous 0' 54 | - 0 is the previous proof, and 0' is the new proof 55 | """ 56 | 57 | proof = 0 58 | while self.valid_proof(last_proof, proof) is False: 59 | proof += 1 60 | 61 | return proof 62 | 63 | @staticmethod 64 | def valid_proof(last_proof: int, proof: int) -> bool: 65 | """ 66 | Validates the Proof 67 | :param last_proof: Previous Proof 68 | :param proof: Current Proof 69 | :return: True if correct, False if not. 70 | """ 71 | 72 | guess = f'{last_proof}{proof}'.encode() 73 | guess_hash = hashlib.sha256(guess).hexdigest() 74 | return guess_hash[:4] == "0000" 75 | 76 | ``` 77 | 78 | 79 | ### Step 2 - Making our blockchain an API endpoint 80 | Add the below code to the bottom of your Python file. This code turns your file into an API end point. This will allow us to use postman to send/receive requests in our blockchain. 81 | 82 | ``` 83 | class Blockchain(object): 84 | 85 | # Instantiate our Node 86 | app = Flask(__name__) 87 | 88 | # Generate a globally unique address for this node 89 | node_identifier = str(uuid4()).replace('-', '') 90 | 91 | # Instantiate the Blockchain 92 | blockchain = Blockchain() 93 | 94 | 95 | @app.route('/mine', methods=['GET']) 96 | def mine(): 97 | return "We'll mine a new Block" 98 | 99 | @app.route('/transactions/new', methods=['POST']) 100 | def new_transaction(): 101 | return "We'll add a new transaction" 102 | 103 | @app.route('/chain', methods=['GET']) 104 | def full_chain(): 105 | response = { 106 | 'chain': blockchain.chain, 107 | 'length': len(blockchain.chain), 108 | } 109 | return jsonify(response), 200 110 | 111 | if __name__ == '__main__': 112 | app.run(host='0.0.0.0', port=5000) 113 | 114 | 115 | @app.route('/transactions/new', methods=['POST']) 116 | def new_transaction(): 117 | values = request.get_json() 118 | 119 | # Check that the required fields are in the POST'ed data 120 | required = ['sender', 'recipient', 'amount'] 121 | if not all(k in values for k in required): 122 | return 'Missing values', 400 123 | 124 | # Create a new Transaction 125 | index = blockchain.new_transaction(values['sender'], values['recipient'], values['amount']) 126 | 127 | response = {'message': f'Transaction will be added to Block {index}'} 128 | return jsonify(response), 201 129 | ``` 130 | 131 | ### Step 3 - Creating a miner for your blockchain 132 | The below code will create a miner for your server, This will mine and create a new transaction to be placed on a block in your blockchain. 133 | 134 | Add this code, below your new transaction code. 135 | 136 | ``` 137 | @app.route('/mine', methods=['GET']) 138 | def mine(): 139 | # We run the proof of work algorithm to get the next proof... 140 | last_block = blockchain.last_block 141 | last_proof = last_block['proof'] 142 | proof = blockchain.proof_of_work(last_proof) 143 | 144 | # We must receive a reward for finding the proof. 145 | # The sender is "0" to signify that this node has mined a new coin. 146 | blockchain.new_transaction( 147 | sender="0", 148 | recipient=node_identifier, 149 | amount=1, 150 | ) 151 | 152 | # Forge the new Block by adding it to the chain 153 | block = blockchain.new_block(proof=proof, previous_hash=0) 154 | 155 | response = { 156 | 'message': "New Block Forged", 157 | 'index': block['index'], 158 | 'transactions': block['transactions'], 159 | 'proof': block['proof'], 160 | 'previous_hash': block['previous_hash'], 161 | } 162 | return jsonify(response), 200 163 | ``` 164 | 165 | ### Step 4 - Interacting with the blockchain 166 | Start up your server. 167 | 168 | Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) 169 | Now open up postman, at the top of postman you will see a search bar. Make sure the button to the left of it is set to GET. 170 | 171 | Then type into the bar http://localhost:5000/mine 172 | --------------------------------------------------------------------------------