diff --git a/DPOS/blockChain.py b/DPOS/blockChain.py new file mode 100644 index 0000000..b793f5d --- /dev/null +++ b/DPOS/blockChain.py @@ -0,0 +1,163 @@ +# Implementation of a simple decentralized blockchain class with multiple nodes and DPoS consensus in the network + +import hashlib +import json +from datetime import datetime +from urllib.parse import urlparse +import requests +from random import randint + + +# Blockchain class +class Blockchain(object): + + # Constructor which creates lists to store the blockchain and the transactions + def __init__(self): + + # List to store the blockchain + self.chain = [] + + # List to store the unverified transactions + self.unverified_transactions = [] + + # List to store verified transactions + self.verified_transactions = [] + + # Genesis block + self.new_block(previous_hash=1) + + # Set containing the nodes in the network. Used set here to prevent the same node getting added again. + self.nodes = set() + + # List containing all the nodes along with their stake in the network + self.all_nodes = [] + + # List of all the voting nodes in the network + self.voteNodespool = [] + + # List which stores all the nodes in descending order of votes received + self.starNodespool = [] + + # List to store the top 3 nodes with the highest (stake * votes_received) + self.superNodespool = [] + + # List to store the address of the delegate nodes selected for mining process + self.delegates = [] + + # Method to create a new block in the Blockchain + def new_block(self, previous_hash=None): + block = { + 'index': len(self.chain) + 1, + 'timestamp': datetime.now().strftime('%Y-%m-%d %H:%M:%S'), + 'transactions': self.unverified_transactions, + 'previous_hash': previous_hash or self.hash(self.chain[-1]) + } + self.verified_transactions += self.unverified_transactions + print(self.verified_transactions) + self.unverified_transactions = [] + + # appending the block at the end of the blockchain + self.chain.append(block) + return block + + # Method to add a new transaction in the next block + def new_transaction(self, sender, item_name, bill_amount): + self.unverified_transactions.append({ + 'Customer name': sender, + 'Recipient': "Dexter's Coffee Shop", + 'Item name': item_name, + 'Total billing amount': bill_amount, + 'timestamp': datetime.now().strftime('%Y-%m-%d %H:%M:%S') + }) + return self.last_block['index'] + 1 + + @property + def last_block(self): + return self.chain[-1] + + # Static method to create an SHA-256 Hash of a given block + @staticmethod + def hash(block): + block_string = json.dumps(block, sort_keys=True).encode() + hash_val = hashlib.sha256(block_string).hexdigest() + return hash_val + + # Method to add node using its IP address to our Blockchain network. + def add_node(self, address, stake): + parsed_url = urlparse(address) + authority = stake + self.nodes.add((parsed_url.netloc, authority)) + + # Method to simulate the voting process + def add_vote(self): + self.all_nodes = list(self.nodes) + + for x in self.all_nodes: + y = list(x) + y.append(x[1] * randint(0, 100)) + self.voteNodespool.append(y) + + print(self.voteNodespool) + + # Method to select top three nodes based on voting results + def selection(self): + self.starNodespool = sorted(self.voteNodespool, key=lambda vote: vote[2], reverse=True) + print(self.starNodespool) + + for x in range(3): + self.superNodespool.append(self.starNodespool[x]) + print(self.superNodespool) + + for y in self.superNodespool: + self.delegates.append(y[0]) + print(self.delegates) + + # Method to sync the list + def sync(self): + r = requests.get('http://localhost:5000/delegates/show') + print(r) + + if r.status_code == 200: + delegates = r.json()['node_delegates'] + self.delegates = delegates[0:3] + print(self.delegates) + + # Method to check if the chain is validated. + def valid_chain(self, chain): + last_block = chain[0] + current_index = 1 + + while current_index < len(chain): + block = chain[current_index] + + # If the hash value of the current block isn't correct then return false + if block['previous_hash'] != self.hash(last_block): + return False + + last_block = block + current_index += 1 + + return True + + # Method to replace the blockchain with the longest validated chain in the network. + def resolve_chain(self): + neighbours = self.nodes + new_chain = None + max_length = len(self.chain) + + for node in neighbours: + response = requests.get(f'http://{node}/chain') + + if response.status_code == 200: + length = response.json()['length'] + chain = response.json()['chain'] + + if length > max_length and self.valid_chain(chain): + max_length = length + new_chain = chain + + if new_chain: + self.chain = new_chain + return True + + return False