from classes.Transaction import Tx from classes.TxBlock import TxBlock from helpers import SignatureHelper as signatureHelper from helpers import UtilityHelper as utilityHelper import time MIN_MINING_TIME = 0 MAX_MINING_TIME = 20 def transaction(self): receiver = input("Enter the username of the receiver:") if receiver == "": return False receiver_data = self.db.fetchUserByUsername(receiver) if not receiver_data: print("Username not found") return False if receiver_data[1] == self.user.public_ser: utilityHelper.clearScreen() print("You cant send money to yourself") return False amount = input("Enter the amount excluding fees:") if amount == "": return False try: amount = float(amount) except: print("Wrong input, try again") return False fee = input("Enter fee:") if fee == "": return False try: fee = float(fee) except: print("Wrong input, try again") return False utilityHelper.clearScreen() print(f"Processing transaction of {amount + fee} coins, {receiver} will receive {amount} coins") if amount + fee > getBalanceWithOutgoingPool(self, getBalance(self)): print("You dont have enough money") return False new_tx = Tx() new_tx.createTransaction(self.user.public_ser, self.user.private_ser, amount, fee, receiver_data[1]) return new_tx 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("You can only mine a block every 3 minutes") return False transactions = Tx() transactions = utilityHelper.loadFile("../data/transaction_pool.dat") if len(transactions) < 5: print("You need atleast 5 transactions to mine a block") return False print("Select items out the pool to mine a block, last 2 values added to list.") 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] = transaction.inputs[0][1] - transaction.outputs[0][1] 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 == "": 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("Wrong input, try again") return False selected_transactions.append(int(i)) except: utilityHelper.clearScreen() print("Wrong input, try again") return False if len(selected_transactions) > 10: print("You can only select up to 10 transactions") return False if len(selected_transactions) < 5: print("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("You need atleast 1 transaction from another user") return False if lastBlock == []: lastBlock = None block = TxBlock(lastBlock) # create block for i in selected_transactions: block.addTx(transactions[i]) start = time.time() nonce = block.find_nonce() elapsed = time.time() - start utilityHelper.clearScreen() if not block.good_nonce(): print("ERROR! Bad nonce") return False print("Success! Nonce is good!") print(f'Accepted Nonce = {str(nonce)}') print("elapsed time: " + str(elapsed) + " s.") if elapsed < MIN_MINING_TIME: print("Mining declined, too fast") return False elif elapsed > MAX_MINING_TIME: print("Mining declined, too Slow") return False # add block to blockchain block.time = elapsed block.nonce = nonce block.date = time.time() block.blockHash = block.computeHash() utilityHelper.saveFile("../data/ledger.dat", block) # TODO remove transactions from transaction pool utilityHelper.resetFile("../data/transaction_pool.dat") transaction_count = 0 for transaction in transactions: if transaction_count not in selected_transactions: utilityHelper.saveFile("../data/transaction_pool.dat", transaction) transaction_count += 1 return True 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 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("Wrong input, try again") return if user_input > len(blocks) - 1 or user_input < 0: utilityHelper.clearScreen() print("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))}") print(f"Block hash: {blocks[user_input].blockHash}") print(f"Block nonce: {blocks[user_input].nonce}") 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": print("Validating block... TODO") 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("Wrong input, try again") return def getBalance(self): balance = 0 blocks = utilityHelper.loadFile("../data/ledger.dat") for block in blocks: for transaction in block.data: if transaction.outputs[0][0] == self.user.public_ser: balance += transaction.outputs[0][1] elif transaction.type != 1 and transaction.inputs[0][0] == self.user.public_ser: balance -= transaction.inputs[0][1] return balance def getBalanceWithOutgoingPool(self, balance): transactions = utilityHelper.loadFile("../data/transaction_pool.dat") for transaction in transactions: if transaction.type != 1 and transaction.inputs[0][0] == self.user.public_ser: balance -= transaction.inputs[0][1] return balance def getPersonalTransactions(self): blocks = utilityHelper.loadFile("../data/ledger.dat") total = 0 total_input = 0 total_output = 0 for block in blocks: for transaction in block.data: if transaction.outputs[0][0] == self.user.public_ser or (transaction.type == 0 and transaction.inputs[0][0] == self.user.public_ser): total += 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"---------------------------------------{total}-------------------------------------------") 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]}") if transaction.outputs[0][0] == self.user.public_ser: total_output += transaction.outputs[0][1] elif transaction.inputs[0][0] == self.user.public_ser: total_input += transaction.inputs[0][1] print("-----------------------------------------------------------------------------------") print(f"Total transactions: {total}") print(f"Total spent: {total_input}") print(f"Total received: {total_output}") print(f"Total balance: {total_output - total_input}") print("-----------------------------------------------------------------------------------") def pendingTransactions(self): transactions = utilityHelper.loadFile("../data/transaction_pool.dat") total = 0 total_amount = 0 user_transactions = {} for transaction in transactions: # continue if transaction is a reward if transaction.type != 0: continue if transaction.inputs[0][0] == self.user.public_ser: total += 1 print(f"---------------------------------------{total}-------------------------------------------") print(f"Transaction input: {transaction.inputs[0][1]}") print(f"Transaction output: {transaction.outputs[0][1]}") print(f"Transaction fees: {transaction.inputs[0][1] - transaction.outputs[0][1]}") print(f"Transaction sender: \n{transaction.inputs[0][0]}") print(f"Transaction recipient: \n{transaction.outputs[0][0]}") total_amount += transaction.inputs[0][1] user_transactions[total] = transaction if total == 0: print("No transactions found") return False print("-----------------------------------------------------------------------------------") print(f"Total transactions: {total}") print(f"Total spent: {total_amount}") print("-----------------------------------------------------------------------------------") print("0 -- Go back") print("1 -- Cancel/Modify transaction") choice = input(">>: ") match choice: case "0": utilityHelper.clearScreen() return case "1": print("Select a transaction to modify/cancel") user_input = input(">>: ") try: user_input = int(user_input) except: utilityHelper.clearScreen() print("Wrong input, try again") return if user_input > total or user_input < 1: utilityHelper.clearScreen() print("Wrong input, try again") return if user_transactions[user_input].type == 1: utilityHelper.clearScreen() print("You cant modify/cancel a reward transaction") return # print info of that transaction utilityHelper.clearScreen() print(f"---------------------------------------{str(user_input)}-------------------------------------------") print(f"Transaction input: {user_transactions.get(user_input).inputs[0][1]}") print(f"Transaction output: {user_transactions.get(user_input).outputs[0][1]}") print(f"Transaction fees: {user_transactions.get(user_input).inputs[0][1] - user_transactions.get(user_input).outputs[0][1]}") print(f"Transaction sender: \n{user_transactions.get(user_input).inputs[0][0]}") print(f"Transaction recipient: \n{user_transactions.get(user_input).outputs[0][0]}") print(f"---------------------------------------END-------------------------------------------") print("Modifying a transaction will bring you back in line, your transaction will lose the place in the pool.") print("0 -- Go back") print("1 -- Change amount") print("2 -- Change Recipient") print("3 -- Cancel transaction") choice = input(">>: ") match choice: case "0": utilityHelper.clearScreen() return case "1": utilityHelper.clearScreen() print("Enter a new amount") amount = input(">>: ") try: amount = float(amount) except: utilityHelper.clearScreen() print("Wrong input, try again") return print("Enter a new fee") fee = input(">>: ") try: fee = float(fee) except: utilityHelper.clearScreen() print("Wrong input, try again") return if amount + fee > getBalanceWithOutgoingPool(self, getBalance(self)): utilityHelper.clearScreen() print("You dont have enough money") print("Nothing happened to the old transaction") return False new_tx = Tx() new_tx.createTransaction(self.user.public_ser, self.user.private_ser, amount, fee, user_transactions[user_input].outputs[0][0]) # remove old transaction from array transactions.remove(user_transactions[user_input]) utilityHelper.resetFile("../data/transaction_pool.dat") # add old transactions to pool for transaction in transactions: utilityHelper.saveFile("../data/transaction_pool.dat", transaction) utilityHelper.saveFile("../data/transaction_pool.dat", new_tx) utilityHelper.clearScreen() print("Transaction modified") return case "2": print("Select a new recipient") recipient = input(">>: ") recipient_data = self.db.fetchUserByUsername(recipient) if not recipient_data: utilityHelper.clearScreen() print("Username not found") return False if recipient_data[1] == self.user.public_ser: utilityHelper.clearScreen() print("You cant send money to yourself") return False new_tx = Tx() new_tx.createTransaction(self.user.public_ser, self.user.private_ser, user_transactions[user_input].inputs[0][1], user_transactions[user_input].inputs[0][1] - user_transactions[user_input].outputs[0][1], recipient_data[1]) # remove old transaction from array transactions.remove(user_transactions[user_input]) utilityHelper.resetFile("../data/transaction_pool.dat") # add old transactions to pool for transaction in transactions: print(transaction) utilityHelper.saveFile("../data/transaction_pool.dat", transaction) utilityHelper.saveFile("../data/transaction_pool.dat", new_tx) utilityHelper.clearScreen() print("Transaction modified") return case "3": # remove old transaction from array transactions.remove(user_transactions[user_input]) utilityHelper.resetFile("../data/transaction_pool.dat") # add old transactions to pool for transaction in transactions: utilityHelper.saveFile("../data/transaction_pool.dat", transaction) utilityHelper.clearScreen() print("Transaction cancelled") return case _: utilityHelper.clearScreen() print("Wrong input, try again") return case _: utilityHelper.clearScreen() print("Wrong input, try again") return