diff --git a/goodchain/src/helpers/BlockHelper.py b/goodchain/src/helpers/BlockHelper.py index e69de29..1dee031 100644 --- a/goodchain/src/helpers/BlockHelper.py +++ b/goodchain/src/helpers/BlockHelper.py @@ -0,0 +1,225 @@ +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 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 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 + if lastBlock == []: + block.id = 0 + else: + block.id = lastBlock.id + 1 + + block.metadata['miner'] = self.user.public_ser + block.metadata['validated'] = False + block.metadata['true_validations'] = 0 + block.metadata['false_validations'] = 0 + + block.time = elapsed + block.nonce = nonce + block.date = time.time() + block.blockHash = block.computeHash() + + utilityHelper.saveFile("../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.saveFile("../data/transaction_pool.dat", transaction) + transaction_count += 1 + + return True \ No newline at end of file diff --git a/goodchain/src/helpers/MenuHelper.py b/goodchain/src/helpers/MenuHelper.py index 7a22586..c2ed949 100644 --- a/goodchain/src/helpers/MenuHelper.py +++ b/goodchain/src/helpers/MenuHelper.py @@ -1,5 +1,6 @@ from classes.User import User from classes.Transaction import Tx +from helpers import TransactionHelper as transactionHelper from helpers import BlockHelper as blockHelper from helpers import TaskHelper as taskHelper from helpers import UtilityHelper as utilityHelper @@ -70,7 +71,7 @@ class MenuHelper: exit() case "Explore the Blockchain": - taskHelper.exploreBlocks(self) + blockHelper.exploreBlocks(self) case "Login": self.user = User(self.db) @@ -140,16 +141,16 @@ class MenuHelper: return case "Explore the Blockchain": - taskHelper.exploreBlocks(self) + blockHelper.exploreBlocks(self) case "Pending transactions": - taskHelper.pendingTransactions(self) + transactionHelper.pendingTransactions(self) case "View personal transactions": taskHelper.getPersonalTransactions(self) case "Transfer coins": - new_tx = taskHelper.transaction(self) + new_tx = transactionHelper.transaction(self) if new_tx != False and new_tx.is_valid(): print("Transaction is valid") utilityHelper.saveFile("../data/transaction_pool.dat", new_tx) @@ -175,7 +176,7 @@ class MenuHelper: print("---------------------------------------END-------------------------------------------") case "Mine a block": - taskHelper.createBlock(self) + blockHelper.createBlock(self) case _: print("Wrong input, try again") diff --git a/goodchain/src/helpers/TaskHelper.py b/goodchain/src/helpers/TaskHelper.py index 29d9690..6065394 100644 --- a/goodchain/src/helpers/TaskHelper.py +++ b/goodchain/src/helpers/TaskHelper.py @@ -1,268 +1,6 @@ -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") @@ -316,173 +54,3 @@ def getPersonalTransactions(self): 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 \ No newline at end of file