from classes.Transaction import Tx from classes.TxBlock import TxBlock from helpers import UtilityHelper as utilityHelper import time from helpers import SocketHelper as socketHelper MIN_MINING_TIME = 10 MAX_MINING_TIME = 20 def exploreBlocks(self): blocks = utilityHelper.loadFile("../data/ledger.dat") x = 0 total_transactions = 0 for block in blocks: x += 1 total_transactions += len(block.data) print(f"---------------------------------------{x}-------------------------------------------") print(block) print(f"Block id: {block.id}") print(f"Block created: {time.strftime('%Y-%m-%d', time.localtime(block.date))}") print("---------------------------------------END-------------------------------------------") print(f"Total blocks: {x}") print(f"Total transactions: {total_transactions}") print("-----------------------------------------------------------------------------------") print("Select a number to view the block, keep empty to return.") user_input = input(">>: ") if user_input == "": utilityHelper.clearScreen() return try: user_input = int(user_input) -1 except: utilityHelper.clearScreen() print(f"{utilityHelper.warningMessage('Wrong input, try again')}") return if user_input > len(blocks) - 1 or user_input < 0: utilityHelper.clearScreen() print(f"{utilityHelper.warningMessage('Wrong input, try again')}") return # print all info of the block utilityHelper.clearScreen() print(f"---------------------------------------{user_input +1}-------------------------------------------") print(f"Block created: {time.strftime('%Y-%m-%d', time.localtime(blocks[user_input].date))}") if blocks[user_input].metadata['validated']: print(f"Block validated: True") else: print(f"Block validated: False, {blocks[user_input].metadata['true_validations']} valid flags, {blocks[user_input].metadata['false_validations']} invalid flags.") print(f"Block id: {blocks[user_input].id}") print(f"Block hash: {blocks[user_input].blockHash}") print(f"Block nonce: {blocks[user_input].nonce}") print(f"Block miner: {blocks[user_input].metadata['miner']}") print(f"Block mined time: {blocks[user_input].time}") print(f"Block transactions: {len(blocks[user_input].data)}") print(f"Block previous hash: {blocks[user_input].previousHash}") print("---------------------------------------------------------------------------------------") print("0 -- Go back") print("1 -- Validate block") print("2 -- View transactions of current block") print("3 -- View another block") choice = input(">>: ") match choice: case "0": utilityHelper.clearScreen() return case "1": utilityHelper.clearScreen() if blocks[user_input].is_valid(): print(f"{utilityHelper.successMessage('This block is valid')}") else: print(f"{utilityHelper.warningMessage('This block is invalid')}") case "2": utilityHelper.clearScreen() x = 0 for transaction in blocks[user_input].data: x += 1 if transaction.type == 1: input_amount = 0 input_user = "Reward" fee = 0 else: input_user = transaction.inputs[0][0] input_amount = transaction.inputs[0][1] fee = input_amount - transaction.outputs[0][1] print(f"---------------------------------------{x}-------------------------------------------") print(f"Transaction input: {input_amount}") print(f"Transaction output: {transaction.outputs[0][1]}") print(f"Transaction fees: {fee}") print(f"Transaction sender: \n{input_user}") print(f"Transaction recipient: \n{transaction.outputs[0][0]}") print("-----------------------------------------------------------------------------------") case "3": utilityHelper.clearScreen() exploreBlocks(self) case _: utilityHelper.clearScreen() print(f"{utilityHelper.warningMessage('Wrong input, try again')}") return def createBlock(self): # add last block if its available try: lastBlock = utilityHelper.loadFile("../data/ledger.dat") except: lastBlock = [] if lastBlock != []: lastBlock = lastBlock[-1] if lastBlock != None and time.time() - lastBlock.date < 180: print(f"{utilityHelper.bcolors.WARNING}You can only mine a block every 3 minutes. Time left: {int(180 - (time.time() - lastBlock.date))}{utilityHelper.bcolors.ENDC}") return False if lastBlock != None and lastBlock.metadata['validated'] == False: print(f"{utilityHelper.warningMessage('You can only mine a block if the last block is validated')}") return False transactions = Tx() transactions = utilityHelper.loadFile("../data/transaction_pool.dat") if len(transactions) < 5: print(f"{utilityHelper.warningMessage('You need atleast 5 transactions to mine a block')}") return False print("Select items out the pool to mine a block, 2 values already selected.") if input("Press enter to continue... (type b to return)") == "b": return False utilityHelper.clearScreen() x = 0 fees = {} for transaction in transactions: if transaction.type == 1: fees[x] = 0 else: fees[x] = round(transaction.inputs[0][1] - transaction.outputs[0][1], 2) x+=1 fees_list = list(fees.keys()) selected_transactions = fees_list[:2] available_transactions = fees_list[2:] # print fees with values and keys if key is in available_transactions for key, value in fees.items(): if key in available_transactions: print(f"{key} -- fee: {value}") # get user input print("Enter the numbers of the transactions you want to mine, seperated by a comma. (ex: 1,2,3)") user_input = input(">>: ") if user_input == "": utilityHelper.clearScreen() return False # seperate user input by comma, check if all the values are in available_transactions user_input = set(user_input.split(",")) for i in user_input: try: if int(i) not in available_transactions: print(f"{utilityHelper.warningMessage('Wrong input, try again')}") return False selected_transactions.append(int(i)) except: utilityHelper.clearScreen() print(f"{utilityHelper.warningMessage('Wrong input, try again')}") return False if len(selected_transactions) > 10: print(f"{utilityHelper.warningMessage('You can only select up to 10 transactions')}") return False if len(selected_transactions) < 5: print(f"{utilityHelper.warningMessage('You need atleast 5 transactions to mine a block')}") return False only_personal = True # check if the user selected more user id's in the selected list for i in selected_transactions[2:]: if transactions[i].type == 1 and transactions[i].outputs[0][0] != self.user.public_ser: only_personal = False elif transactions[i].type == 0 and transactions[i].inputs[0][0] != self.user.public_ser: only_personal = False if only_personal: print(f"{utilityHelper.warningMessage('You need atleast 1 transaction from another user')}") return False if lastBlock == []: lastBlock = None block = TxBlock(lastBlock) # create block for i in selected_transactions: if not transactions[i].is_valid(): self.db.createLog(transactions[i].inputs[0][0], time.time(), f"Transaction of {transactions[i].outputs[0][1]} with {transactions[i].inputs[0][1] - transactions[i].outputs[0][1]} coins fee is declined") try: del transactions[i] except: return False for transaction in transactions: utilityHelper.addFile("../data/transaction_pool.dat", transaction) print(f"{utilityHelper.errorMessage('ERROR! Invalid transaction found. transaction removed from pool. Please restart mining a block...')}") return False block.addTx(transactions[i]) start = time.time() nonce = block.find_nonce() elapsed = time.time() - start utilityHelper.clearScreen() if not block.good_nonce(): print(f"{utilityHelper.errorMessage('ERROR! Bad nonce')}") return False print(f"{utilityHelper.successMessage('Success! Nonce is good!')}") print(f'Accepted Nonce = {str(nonce)}') print("elapsed time: " + str(elapsed) + " s.") if elapsed < MIN_MINING_TIME: print(f"{utilityHelper.errorMessage('Mining declined, too fast')}") return False elif elapsed > MAX_MINING_TIME: print(f"{utilityHelper.errorMessage('Mining declined, too Slow')}") return False # add block to blockchain if lastBlock == None: block.id = 0 else: block.id = lastBlock.id + 1 block.metadata['miner'] = self.user.public_ser block.metadata['validated'] = False block.metadata['validated_by'] = [] block.metadata['true_validations'] = 0 block.metadata['false_validations'] = 0 block.time = elapsed block.nonce = nonce block.date = time.time() block.blockHash = block.computeHash() # NEW SEND BLOCK socketHelper.sendObj(self.peer_ip_addr, block, self.peer_port) utilityHelper.addFile("../data/ledger.dat", block) utilityHelper.resetFile("../data/transaction_pool.dat") transaction_count = 0 for transaction in transactions: if transaction_count not in selected_transactions: utilityHelper.addFile("../data/transaction_pool.dat", transaction) transaction_count += 1 # print fee amount fees = 0 for i in selected_transactions: if transactions[i].type != 1: fees += transactions[i].inputs[0][1] - transactions[i].outputs[0][1] fees = round(fees, 2) print(f"After validating you will receive a reward of {fees + 25} coins!") return True def validateMinedBlock(self): blocks = utilityHelper.loadFile("../data/ledger.dat") if blocks == []: return False new_block = blocks[-1] if new_block.metadata['validated']: return False if new_block.metadata['miner'] == self.user.public_ser: return False if self.user.public_ser in new_block.metadata['validated_by']: return False block_status = new_block.is_valid() new_block.metadata['validated_by'].append(self.user.public_ser) if block_status: new_block.metadata['true_validations'] += 1 if new_block.metadata['true_validations'] >= 3: new_block.metadata['validated'] = True # get fees from transactions in block fees = 0 for transaction in new_block.data: if transaction.type != 1: fees += transaction.inputs[0][1] - transaction.outputs[0][1] self.db.createLog(transaction.inputs[0][0], time.time(), f"Transaction of {transaction.inputs[0][1]} is added in block id {new_block.id}") self.db.createLog(transaction.outputs[0][0], time.time(), f"You have received new coins: {transaction.outputs[0][1]}, To see all info view block id {new_block.id}") fees = round(fees, 2) new_reward = Tx() new_reward.createRewardTransaction(new_block.metadata['miner'], self.user.private_ser, "MINE", fees) self.db.createLog(new_block.metadata['miner'], time.time(), f"Block id {new_block.id}, is validated! You received a reward of {fees + 25} coins!") utilityHelper.addFile("../data/transaction_pool.dat", new_reward) try: del blocks[-1] except: return False utilityHelper.resetFile("../data/ledger.dat") blocks.append(new_block) # NEW SEND BLOCK socketHelper.sendObj(self.peer_ip_addr, new_block, self.peer_port) # NEW SEND TRANSACTION socketHelper.sendObj(self.peer_ip_addr, new_reward, self.peer_port) for block in blocks: utilityHelper.addFile("../data/ledger.dat", block) return True else: new_block.metadata['false_validations'] += 1 if new_block.metadata['false_validations'] >= 3: # Create log for miner self.db.createLog(new_block.metadata['miner'], time.time(), f"Block id {new_block.id}, was flagged invalid. Mine unsuccesful...") # setsaveFil transactions in block back to the pool for transaction in new_block.data: utilityHelper.addFile("../data/transaction_pool.dat", transaction) try: del blocks[-1] except: print(f"{utilityHelper.errorMessage('Something went wrong')}") return False utilityHelper.resetFile("../data/ledger.dat") # NEW SEND BLOCK socketHelper.sendObj(self.peer_ip_addr, new_block, self.peer_port) for block in blocks: utilityHelper.addFile("../data/ledger.dat", block) return True