315 lines
12 KiB
Python
315 lines
12 KiB
Python
from classes.Transaction import Tx
|
|
from classes.TxBlock import TxBlock
|
|
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(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":
|
|
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(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] = 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 == "":
|
|
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 transaction[i].is_valid():
|
|
# # Return transaction as 'invalid' transaction
|
|
# # stop mining with the error "Invalid transaction found. transaction removed from pool. Please restart mining a block..."
|
|
# pass
|
|
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()
|
|
|
|
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
|
|
|
|
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}")
|
|
|
|
new_reward = Tx()
|
|
new_reward.createRewardTransaction(self.user.public_ser, self.user.private_ser, "MINE", fees)
|
|
self.db.createLog(new_block.metadata['miner'], time.time(), f"Block id {new_block.id}, is validated!")
|
|
utilityHelper.saveFile("../data/transaction_pool.dat", new_reward)
|
|
|
|
|
|
try:
|
|
del blocks[-1]
|
|
except:
|
|
return False
|
|
|
|
utilityHelper.resetFile("../data/ledger.dat")
|
|
blocks.append(new_block)
|
|
for block in blocks:
|
|
utilityHelper.saveFile("../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...")
|
|
# set transactions in block back to the pool
|
|
for transaction in new_block.data:
|
|
utilityHelper.saveFile("../data/transaction_pool.dat", transaction)
|
|
try:
|
|
del blocks[-1]
|
|
except:
|
|
print(f"{utilityHelper.errorMessage('Something went wrong')}")
|
|
return False
|
|
|
|
utilityHelper.resetFile("../data/ledger.dat")
|
|
for block in blocks:
|
|
utilityHelper.saveFile("../data/ledger.dat", block)
|
|
|
|
return True
|
|
|
|
|
|
|