diff --git a/alex.keys b/alex.keys new file mode 100644 index 0000000..0d5f7d7 Binary files /dev/null and b/alex.keys differ diff --git a/block.dat b/block.dat new file mode 100644 index 0000000..74ee6ec Binary files /dev/null and b/block.dat differ diff --git a/goodchain/data/block.dat b/goodchain/data/block.dat new file mode 100644 index 0000000..74ee6ec Binary files /dev/null and b/goodchain/data/block.dat differ diff --git a/goodchain/data/goodchain.db b/goodchain/data/goodchain.db new file mode 100644 index 0000000..5ca8c75 Binary files /dev/null and b/goodchain/data/goodchain.db differ diff --git a/goodchain/data/transaction_pool.dat b/goodchain/data/transaction_pool.dat new file mode 100644 index 0000000..e69de29 diff --git a/goodchain/data/transaction_pool.txt b/goodchain/data/transaction_pool.txt new file mode 100644 index 0000000..e69de29 diff --git a/goodchain/src/classes/Blockchain.py b/goodchain/src/classes/Blockchain.py new file mode 100644 index 0000000..dcfd8ad --- /dev/null +++ b/goodchain/src/classes/Blockchain.py @@ -0,0 +1,25 @@ +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import hashes + +class CBlock: + + data = None + previousHash = None + previousBlock = None + def __init__(self, data, previousBlock): + self.data = data + self.blockHash = None + self.previousBlock = previousBlock + if previousBlock != None: + self.previousHash = previousBlock.computeHash() + + def computeHash(self): + digest = hashes.Hash(hashes.SHA256(), backend=default_backend()) + digest.update(bytes(str(self.data),'utf8')) + digest.update(bytes(str(self.previousHash),'utf8')) + return digest.finalize() + + def is_valid(self): + if self.previousBlock == None: + return True + return self.previousBlock.computeHash() == self.previousHash \ No newline at end of file diff --git a/goodchain/src/classes/Transaction.py b/goodchain/src/classes/Transaction.py new file mode 100644 index 0000000..7a55f27 --- /dev/null +++ b/goodchain/src/classes/Transaction.py @@ -0,0 +1,110 @@ +MINE_REWARD_VALUE = 25.0 +REGISTRATION_REWARD_VALUE = 50.0 +NORMAL = 0 +REWARD = 1 + +from helpers import SignatureHelper as Signature + +class Tx: + def __init__(self, type = NORMAL): + + self.type = type + self.inputs = [] + self.outputs = [] + self.sigs = [] + self.reqd = [] + + def add_input(self, from_addr, amount): + self.inputs.append((from_addr, amount)) + + def add_output(self, to_addr, amount): + self.outputs.append((to_addr, amount)) + + def add_reqd(self, addr): + self.reqd.append(addr) + + def sign(self, private): + message = self.__gather() + newsig = Signature.sign(message, private) + self.sigs.append(newsig) + + def is_valid(self): + + if self.type == REWARD: + if len(self.inputs)!=0 and len(self.outputs)!=1: + return False + return True + + else: + total_in = 0 + total_out = 0 + message = self.__gather() + for addr,amount in self.inputs: + found = False + for s in self.sigs: + if Signature.verify(message, s, addr): + found = True + if not found: + return False + if amount < 0: + return False + total_in = total_in + amount + for addr in self.reqd: + found = False + for s in self.sigs: + if Signature.verify(message, s, addr): + found = True + if not found: + return False + for addr,amount in self.outputs: + if amount < 0: + return False + total_out = total_out + amount + + if total_out > total_in: + return False + return True + + def __gather(self): + data=[] + data.append(self.inputs) + data.append(self.outputs) + data.append(self.reqd) + return data + + def __repr__(self): + + repr_str = "INPUTS:\n" + for addr, amt in self.inputs: + repr_str = repr_str + str(amt) + "from" + str(addr) + "\n" + + repr_str += "OUTPUTS:\n" + for addr, amt in self.outputs: + repr_str = repr_str + str(amt) + "to" + str(addr) + "\n" + + repr_str += "EXTRA REQUIRED SIGNATURES:\n" + for req_sig in self.reqd: + repr_str = repr_str + str(req_sig) + "\n" + + repr_str += "SIGNATURES:\n" + for sig in self.sigs: + repr_str = repr_str + str(sig) + "\n" + + repr_str += "END\n" + + return repr_str + + def createTransaction(self, public_key, private_key, amount, fee, recipient, reqd = None, type = NORMAL): + self.add_input(public_key, amount + fee) + self.add_output(recipient, amount) + self.type = type + if reqd != None: + self.add_reqd(reqd) + self.sign(private_key) + + def createRewardTransaction(self, public_key, private_key, type): + value = type == "mine" and MINE_REWARD_VALUE or REGISTRATION_REWARD_VALUE + self.add_output(public_key, value) + self.type = REWARD + self.sign(private_key) + diff --git a/goodchain/src/classes/TxBlock.py b/goodchain/src/classes/TxBlock.py new file mode 100644 index 0000000..fe82526 --- /dev/null +++ b/goodchain/src/classes/TxBlock.py @@ -0,0 +1,59 @@ +from classes.Blockchain import CBlock +from helpers.SignatureHelper import * +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.backends import default_backend + +import random + +REWARD_VALUE = 25.0 +leading_zeros = 2 +next_char_limit = 20 + +class TxBlock (CBlock): + + def __init__(self, previousBlock): + self.nonce = "A random nonce" + super(TxBlock, self).__init__([], previousBlock) + + def addTx(self, Tx_in): + self.data.append(Tx_in) + + def __count_totals(self): + total_in = 0 + total_out = 0 + for tx in self.data: + for addr, amt in tx.inputs: + total_in = total_in + amt + for addr, amt in tx.outputs: + total_out = total_out + amt + return total_in, total_out + + def is_valid(self): + if not super(TxBlock, self).is_valid(): + return False + for tx in self.data: + if not tx.is_valid(): + return False + + total_in, total_out = self.__count_totals() + + Tx_Balance = round(total_out - total_in, 10) + + if Tx_Balance > REWARD_VALUE: + return False + return True + + def good_nonce(self): + digest = hashes.Hash(hashes.SHA256(), backend=default_backend()) + digest.update(bytes(str(self.data), 'utf8')) + digest.update(bytes(str(self.previousHash), 'utf8')) + digest.update(bytes(str(self.nonce), 'utf8')) + this_hash = digest.finalize() + return this_hash[:leading_zeros] == b'\x00'*leading_zeros + + def find_nonce(self): + for i in range(10000000): + self.nonce = i + if self.good_nonce(): + return self.nonce + return None diff --git a/goodchain/src/helpers/BlockHelper.py b/goodchain/src/helpers/BlockHelper.py new file mode 100644 index 0000000..e69de29 diff --git a/goodchain/src/helpers/LoggerHelper.py b/goodchain/src/helpers/LoggerHelper.py deleted file mode 100644 index f9bca2d..0000000 --- a/goodchain/src/helpers/LoggerHelper.py +++ /dev/null @@ -1 +0,0 @@ -# Not sure if we're planning to log anything tho \ No newline at end of file diff --git a/goodchain/src/helpers/MenuHelper.py b/goodchain/src/helpers/MenuHelper.py index 83b5658..4cf064e 100644 --- a/goodchain/src/helpers/MenuHelper.py +++ b/goodchain/src/helpers/MenuHelper.py @@ -1,6 +1,10 @@ import os from classes.User import User +from classes.Transaction import Tx +from helpers import BlockHelper as blockHelper +from helpers import TaskHelper as taskHelper +from helpers import UtilityHelper as utilityHelper class bcolors: HEADER = '\033[95m' @@ -131,10 +135,24 @@ class MenuHelper: case "Explore the Blockchain": print("TODO") + block = blockHelper.loadBlock() + i = 0 + for data in block.data: + + print(f"---------------------------------- Block {str(i)} ----------------------------------") + print(f"Block hash: {data.Inputs[0][1]}") + print(f"----------------------------------------------------------------------------------") + i += 1 case "Transfer coins": - input("Enter the username of the receiver: ") - print("TODO") + new_tx = taskHelper.transaction(self) + if new_tx.is_valid(): + # TODO: add to pool + print("Transaction is valid") + utilityHelper.saveFile("../data/transaction_pool.dat", new_tx) + continue + print("Transaction is invalid") + case "Cancel transaction": print("TODO") diff --git a/goodchain/src/helpers/SignatureHelper.py b/goodchain/src/helpers/SignatureHelper.py index 4c43ee6..fd22c7b 100644 --- a/goodchain/src/helpers/SignatureHelper.py +++ b/goodchain/src/helpers/SignatureHelper.py @@ -41,6 +41,12 @@ def bytesToKeys(private_ser, public_ser): ) return private_key, public_key +def publicBytesToKey(public_ser): + public_key = serialization.load_pem_public_key( + public_ser + ) + return public_key + def sign(message, private_key): message = bytes(str(message), 'utf-8') signature = private_key.sign( @@ -50,9 +56,8 @@ def sign(message, private_key): ) return signature -def verify(message, signature, pbc_ser): +def verify(message, signature, public_key): message = bytes(str(message), 'utf-8') - public_key = serialization.load_pem_public_key(pbc_ser) try: public_key.verify( signature, diff --git a/goodchain/src/helpers/TaskHelper.py b/goodchain/src/helpers/TaskHelper.py new file mode 100644 index 0000000..c993f31 --- /dev/null +++ b/goodchain/src/helpers/TaskHelper.py @@ -0,0 +1,38 @@ +from classes.Transaction import Tx +from helpers import SignatureHelper as signatureHelper + +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 + + receiver_public_key = signatureHelper.publicBytesToKey(receiver_data[1]) + + amount = input("Enter the amount excluding fees:") + if amount == "": + return False + + try: + amount = int(amount) + except: + print("Wrong input, try again") + return False + + fee = input("Enter fee:") + if fee == "": + return False + + try: + fee = int(fee) + except: + print("Wrong input, try again") + return False + + new_tx = Tx() + new_tx.createTransaction(self.user.public_key, self.user.private_key, amount, fee, receiver_public_key) + return new_tx \ No newline at end of file diff --git a/goodchain/src/helpers/UtilityHelper.py b/goodchain/src/helpers/UtilityHelper.py index 1470242..227cfae 100644 --- a/goodchain/src/helpers/UtilityHelper.py +++ b/goodchain/src/helpers/UtilityHelper.py @@ -1,6 +1,4 @@ -from cryptography.hazmat.backends import default_backend -from cryptography.hazmat.primitives import hashes - +import pickle import hashlib def computeHash(data): @@ -8,3 +6,15 @@ def computeHash(data): data = str(data).encode() hash.update(data) return hash.hexdigest() + + +def saveFile(fileloc, data): + savefile = open(fileloc, "wb") + pickle.dump(data, savefile) + savefile.close() + +def loadFile(fileloc): + loadfile = open(fileloc ,"rb") + load = pickle.load(loadfile) + loadfile.close() + return load \ No newline at end of file diff --git a/mike.keys b/mike.keys new file mode 100644 index 0000000..61de7bc Binary files /dev/null and b/mike.keys differ diff --git a/rose.keys b/rose.keys new file mode 100644 index 0000000..17548a1 Binary files /dev/null and b/rose.keys differ