From 2c2082d88df823f4c653b901936a5cbaea62f00e Mon Sep 17 00:00:00 2001 From: Tolga Erdonmez Date: Mon, 26 Aug 2019 18:12:23 +0300 Subject: [PATCH] 7# Initial Commit now transaction fee, and miner bonus is being sent to miners :) for example: after 5 new transaction, new block is being mined. and then sent to other nodes improvements to test files --- tcoin_base/blockchain.py | 13 ++++- tcoin_base/node.py | 102 +++++++++++++++++++++++++------------- tcoin_base/transaction.py | 16 +++++- tcoin_base/wallet.py | 35 ++++++++++--- test_node.py | 7 ++- test_wallet.py | 28 +++++++---- 6 files changed, 145 insertions(+), 56 deletions(-) diff --git a/tcoin_base/blockchain.py b/tcoin_base/blockchain.py index 9bff087..eabc5cc 100644 --- a/tcoin_base/blockchain.py +++ b/tcoin_base/blockchain.py @@ -1,3 +1,4 @@ +from .transaction import Transaction import hashlib import datetime import time @@ -44,7 +45,8 @@ def dict(self): # def from_dict(self): # b = Block() -DIFFICULTY = '0000' +DIFFICULTY = '00' +MINER_BONUS = 10 class Blockchain(): @@ -88,6 +90,15 @@ def create_block(self, miner): nodes = self.current_nodes.copy() transactions = self.current_transactions.copy() + + tx_fee = Transaction.calc_fee(transactions) + if tx_fee: + tx_fee = Transaction(sender = None, receiver = miner, input = tx_fee, output = tx_fee) # creating tx for miners tx_fee + transactions.append(tx_fee.dict()) + + miner_bonus = Transaction(sender = None, receiver = miner, input = MINER_BONUS, output = MINER_BONUS) # creating tx for miner bonus + transactions.append(miner_bonus.dict()) + new_block = Block( index = len(self.chain) + 1, previous_hash = prev_hash, diff --git a/tcoin_base/node.py b/tcoin_base/node.py index 02dd15b..b8e995c 100644 --- a/tcoin_base/node.py +++ b/tcoin_base/node.py @@ -12,9 +12,9 @@ JOIN_MSG = 'JOIN_TCOIN_BLOCKCHAIN' JOIN_INTERVAL = 5 -NEW_BLOCK_MSG = 'NEW_TCOIN_BLOCK' +NEW_BLOCK = 'NEW_TCOIN_BLOCK' GET_CHAIN = 'GET_CHAIN' -SEND_CHAIN_INTERVAL = 10 +SEND_CHAIN_INTERVAL = 25 HEADER_LENGTH = 1000 class NodeClient(socket.socket): @@ -23,9 +23,10 @@ def __init__(self): super().__init__(socket.AF_INET,socket.SOCK_STREAM) def connect_node(self, conn_addr, node_addr): - super().connect(conn_addr) - # self.setblocking(True) try: + super().connect(conn_addr) + # self.setblocking(True) + join_msg = [JOIN_MSG,node_addr] join_msg = pickle.dumps(join_msg) msg = f"{len(join_msg):<{HEADER_LENGTH}}".encode() + join_msg @@ -35,9 +36,9 @@ def connect_node(self, conn_addr, node_addr): return False def send_chain(self, node_addr, conn_addr, chain): - super().connect(conn_addr) - try: + super().connect(conn_addr) + pickled_chain = pickle.dumps(chain) chain_msg = [GET_CHAIN,node_addr,pickled_chain] chain_msg = pickle.dumps(chain_msg) @@ -47,6 +48,18 @@ def send_chain(self, node_addr, conn_addr, chain): except: return False + def send_block(self, node_addr, conn_addr, block): + try: + super().connect(conn_addr) + + pickled_block = pickle.dumps(block) + chain_msg = [NEW_BLOCK, node_addr, pickled_block] + chain_msg = pickle.dumps(chain_msg) + msg = f"{len(chain_msg):<{HEADER_LENGTH}}".encode() + chain_msg + self.send(msg) + except: + pass + class Node(socket.socket): def __init__(self, ip, port, is_miner = False): @@ -60,11 +73,9 @@ def __init__(self, ip, port, is_miner = False): # __init__ Node self.threads = {} - self.is_miner = is_miner self.sending_chain = False self.incoming_clients = LifoQueue() - self.wallet = Wallet.load_wallet_pem(file_path='./2') # loading if already a blockchain exists loaded_chain = Blockchain.load_blockchain((self.IP,self.PORT)) if loaded_chain: @@ -73,6 +84,10 @@ def __init__(self, ip, port, is_miner = False): # creating new blockchain self.blockchain = Blockchain(self) + self.is_miner = is_miner + if self.is_miner: + self.wallet = Wallet.optioned_create_wallet() + # INIT METHODS if self.blockchain.loaded: self.__connect_nodes() @@ -80,17 +95,19 @@ def __init__(self, ip, port, is_miner = False): # Starting threads self.new_job(target=self.get_clients) self.new_job(target=self.communucate_nodes) - # self.new_job(target=self.send_chain) - # if self.is_miner: - # self.new_job(target=self.mine_block) - + self.new_job(target=self.send_chain) + if self.is_miner: + self.new_job(target=self.mine_block) + def mine_block(self): while True: - self.blockchain.create_block((self.IP,self.PORT)) - print('\n\nNew Block Created') - # print(self.blockchain.last_block(),'\n\n') - time.sleep(1) - + if len(self.blockchain.current_transactions) <= 5: + continue + self.blockchain.create_block(self.wallet.pu_ser) + for node in self.blockchain.current_nodes: + client = NodeClient() + client.send_block(node_addr = (self.IP,self.PORT), conn_addr = node, block = self.blockchain.last_block()) + def get_data(self, socket): msg_list = [] while True: @@ -135,6 +152,16 @@ def communucate_nodes(self): # if node is online => add to node list self.blockchain.current_nodes.add(tuple(node_addr)) print(f"New node connected to blockchain!\n Node Address: {node_addr[0]}:{node_addr[1]}") + elif msg_header == NEW_BLOCK: + for msg in node_msg: + new_block = msg[2] + new_block = pickle.loads(new_block) + chain = self.blockchain.chain.copy() + chain.append(new_block) + check = Blockchain.check_chain(chain) + if check: + print('New Block received!') + self.blockchain.chain.append(new_block) # GET_CHAIN gets chain around a period of time from other nodes for replacing elif msg_header == GET_CHAIN: for msg in node_msg: @@ -156,8 +183,13 @@ def communucate_nodes(self): elif msg_header == NEW_TX: for msg in node_msg: new_tx = pickle.loads(msg[1]) - self.blockchain.current_transactions.append(new_tx) - print('New TX !') + tx = Transaction.from_dict(new_tx) + verify = Wallet.verify(tx) + if verify: + self.blockchain.current_transactions.append(new_tx) + print('New TX !') + else: + print('Invalid transaction !') node_client.close() @@ -166,22 +198,22 @@ def pop_nodes(self,nodes): self.blockchain.current_nodes.remove(node) def send_chain(self): - # while True: - # time.sleep(SEND_CHAIN_INTERVAL) - # if len(self.blockchain.current_nodes) - 1 == 0: - # continue - excep_nodes = [] - all_nodes = self.blockchain.current_nodes - for (ip,port) in all_nodes: - if (ip,port) != (self.IP,self.PORT): - try: - client = NodeClient() - client.send_chain(node_addr = (self.IP,self.PORT), conn_addr = (ip,port),chain = self.blockchain.chain) - print('sending chain to: ', ip, port) - client.close() - except: - excep_nodes.append((ip,port)) # this is gonna stay for a while, but actually it is smthg we do not need to happen (losing nodes) - self.pop_nodes(excep_nodes) + while True: + time.sleep(SEND_CHAIN_INTERVAL) + if len(self.blockchain.current_nodes) - 1 == 0: + continue + excep_nodes = [] + all_nodes = self.blockchain.current_nodes + for (ip,port) in all_nodes: + if (ip,port) != (self.IP,self.PORT): + try: + client = NodeClient() + client.send_chain(node_addr = (self.IP,self.PORT), conn_addr = (ip,port),chain = self.blockchain.chain) + print('sending chain to: ', ip, port) + client.close() + except: + excep_nodes.append((ip,port)) # this is gonna stay for a while, but actually it is smthg we do not need to happen (losing nodes) + self.pop_nodes(excep_nodes) def __connect_nodes(self): excep_nodes = [] diff --git a/tcoin_base/transaction.py b/tcoin_base/transaction.py index 493878b..2d51f6a 100644 --- a/tcoin_base/transaction.py +++ b/tcoin_base/transaction.py @@ -31,4 +31,18 @@ def dict(self): def from_dict(dict): tx = Transaction(sender = dict['sender'],receiver = dict['receiver'], input = dict['input'], output = dict['output']) tx.sig = dict['sig'] - return tx \ No newline at end of file + return tx + + @staticmethod + def calc_fee(transactions): + sum = 0 + if len(transactions) > 0: + for tx in transactions: + if type(tx) == type(dict()): + sum += tx['tx_fee'] + else: + sum += tx.tx_fee + return sum + else: + return False + \ No newline at end of file diff --git a/tcoin_base/wallet.py b/tcoin_base/wallet.py index 9db0e57..a164252 100644 --- a/tcoin_base/wallet.py +++ b/tcoin_base/wallet.py @@ -80,7 +80,7 @@ def send_tx(node_addr, tx_data): one_client.send(tx_data) one_client.close() -class Wallet(socket.socket): +class Wallet(): def __init__(self, node_addr = tuple(), pr = None, pu = None): # __init__ Wallet @@ -95,7 +95,7 @@ def __init__(self, node_addr = tuple(), pr = None, pu = None): self.public, self.private = rsa.newkeys(self.__BITS) print('Your Wallet has been created') - self.pu_ser = rsa.pem.save_pem(self.public._save_pkcs1_pem(),'RSA PUBLIC KEY') + self.pu_ser = (rsa.pem.save_pem(self.public._save_pkcs1_pem(),'RSA PUBLIC KEY')).decode() # connect to blockchain if node_addr is given if len(self.node_addr) != 0: @@ -111,9 +111,9 @@ def calculate_coins(self): for block in self.chain: for tx_dict in block.transactions: tx = Transaction.from_dict(tx_dict) - if tx.sender == self.pu_ser.decode(): + if tx.sender == self.pu_ser: minus += tx.input - if tx.receiver == self.pu_ser.decode(): + if tx.receiver == self.pu_ser: plus += tx.output total = plus - minus print(plus,minus) @@ -129,8 +129,8 @@ def sign(self, tx): def send_tx(self, receiver_pu, amount, tx_fee): new_tx = Transaction( - sender = self.pu_ser.decode(), - receiver = receiver_pu.decode(), + sender = self.pu_ser, + receiver = receiver_pu, input = amount + tx_fee, output = amount ) @@ -170,7 +170,12 @@ def new_job(self, target, args = (), daemon = False): t.start() @staticmethod - def verify(data, sig, public_key): + def verify(tx): + # tx obj of class Transaction + public_key = rsa.pem.load_pem(tx.sender,'RSA PUBLIC KEY') + public_key = rsa.PublicKey.load_pkcs1(public_key) + sig = tx.sig + data = str(tx.gather()).encode() # rsa.verify returns the data encryption type it's sha256 for us try: rsa.verify(data, sig, public_key) @@ -189,3 +194,19 @@ def load_wallet_pem(node_addr = tuple(), file_path = './'): return False public_key = rsa.key.PublicKey(private_key.n, private_key.e) return Wallet(node_addr = node_addr,pr = private_key, pu = public_key) + + @staticmethod + def optioned_create_wallet(node_addr = tuple()): + path = input('Path to create or load wallet: ') + if path == '': + path = './' + try_load = Wallet.load_wallet_pem(file_path=path) + if try_load: # if a wallet is loaded, return it + if node_addr: + try_load.node_addr = node_addr + try_load.connect_blockchain() + return try_load + else: + new_wallet = Wallet(node_addr = node_addr) + new_wallet.save_wallet_pem(file_path=path) + return new_wallet \ No newline at end of file diff --git a/test_node.py b/test_node.py index 9cd4f4a..e823eab 100644 --- a/test_node.py +++ b/test_node.py @@ -28,7 +28,7 @@ def cmd(): if cmd == 'check': print(Blockchain.check_chain(n1.blockchain.chain)) if cmd == 'mine': - n1.blockchain.create_block(n1.wallet.pu_ser.decode()) + n1.blockchain.create_block(n1.wallet.pu_ser) if cmd == 'chain': print(len(n1.blockchain.chain)) if cmd == 'nodes': @@ -39,7 +39,10 @@ def cmd(): if cmd == 'send': n1.send_chain() if cmd == 'tx': - print(n1.blockchain.current_transactions) + print(len(n1.blockchain.current_transactions)) + if cmd == 'calc': + n1.wallet.chain = n1.blockchain.chain + print(n1.wallet.calculate_coins()) # for i in range(10): # n1.blockchain.create_block(n1.wallet.pu_ser.decode()) diff --git a/test_wallet.py b/test_wallet.py index 4bffd7b..b9b12db 100644 --- a/test_wallet.py +++ b/test_wallet.py @@ -6,14 +6,12 @@ IP = "127.0.0.1" PORT = int(input('Port: ')) # w = input('wallet: ') -w = None - -# def open_wallet(): -# global w -w = Wallet.load_wallet_pem(node_addr=(IP, PORT), file_path='./') - -w2 = Wallet.load_wallet_pem(file_path='./2') +# w = Wallet.load_wallet_pem(node_addr=(IP, PORT), file_path='./' + w) +# w = Wallet(node_addr=(IP, PORT)) +w = Wallet.optioned_create_wallet((IP, PORT)) +# w2 = Wallet.load_wallet_pem(file_path='./2') +cur_recv = None def new_job(target, args = (), daemon = False): t = threading.Thread(target=target, args = args) t.daemon = daemon @@ -21,21 +19,31 @@ def new_job(target, args = (), daemon = False): def cmd(): global w + global cur_recv + while True: cmd = input('command:') if cmd == 'quit': sys.exit() if cmd == 'tx': # new_job(target=w.send_tx,args=(w2.pu_ser,50,0),daemon=True) - w.send_tx(w2.pu_ser,50,0) + pu = cur_recv.pu_ser + w.send_tx(pu,50,7) if cmd == 'calc': print(w.calculate_coins()) if cmd == 'my_tx': for block in w.chain: for tx in block.transactions: - if tx['receiver'] == w.pu_ser.decode(): + if tx['receiver'] == w.pu_ser: print(tx['input'],tx['output']) if cmd == 'reconn': w.connect_blockchain() - + if cmd == 'pu': + print(w.pu_ser) + if cmd == 'save': + p = input('Save to: ') + w.save_wallet_pem(file_path='./' + p) + if cmd == 'load_recv': + p = input('load from: ') + cur_recv = Wallet.load_wallet_pem(file_path='./' + p) new_job(cmd) \ No newline at end of file