diff --git a/.gitignore b/.gitignore index bdb3bad..70fd299 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ tcoin_base/__pycache__/ venv/ utils/ deneme* -*.pem \ No newline at end of file +*.pem +_* \ No newline at end of file diff --git a/tcoin_base/blockchain.py b/tcoin_base/blockchain.py index d7d305c..611cac6 100644 --- a/tcoin_base/blockchain.py +++ b/tcoin_base/blockchain.py @@ -2,6 +2,7 @@ import datetime import time import pickle +import json def timeit(method): def timed(*args, **kw): @@ -35,6 +36,10 @@ def __str__(self): string = f"index: {self.index}\n hash: {self.hash}\n previous_hash: {self.prev_hash}\n transactions: {str(self.transactions)}\n nodes: {str(self.nodes)}\n proof: {self.proof}\n miner: {self.miner}" return string + def dict(self): + dict = {'index':self.index, 'timestamp':self.timestamp, 'miner':self.miner, 'hash':self.hash, 'prev_hash': self.prev_hash, 'proof':self.proof, 'nodes':list(self.nodes), 'transactions':self.transactions} + return dict + DIFFICULTY = '0000' class Blockchain(): @@ -94,6 +99,8 @@ def create_block(self, miner): self.save_blockchain() def last_block(self): + if len(self.chain) == 0: + return None return self.chain[-1] # @timeit @@ -151,3 +158,14 @@ def __str__(self): string += str(block) + '\n' return string + def dict(self): + dict = {'chain':[],'length':len(self.chain)} + for block in self.chain: + dict_tx = [] + for tx in block.transactions: + print(tx) + dict_tx.append(tx.dict()) + block.transactions = dict_tx + dict['chain'].append(block.dict()) + print(dict) + return dict \ No newline at end of file diff --git a/tcoin_base/node.py b/tcoin_base/node.py index 533f238..6af54fd 100644 --- a/tcoin_base/node.py +++ b/tcoin_base/node.py @@ -1,6 +1,6 @@ from .blockchain import Blockchain, Block from .transaction import Transaction -from .wallet import WALLET_CHAIN, NEW_TX +from .wallet import Wallet, WALLET_CHAIN, NEW_TX import socket import select import threading @@ -15,7 +15,7 @@ NEW_BLOCK_MSG = 'NEW_TCOIN_BLOCK' GET_CHAIN = 'GET_CHAIN' SEND_CHAIN_INTERVAL = 10 -HEADER_LENGTH = 100 +HEADER_LENGTH = 1000 class NodeClient(socket.socket): @@ -58,13 +58,14 @@ def __init__(self, ip, port, is_miner = False): super().__init__(socket.AF_INET,socket.SOCK_STREAM) self.bind((self.IP, self.PORT)) self.listen() - + # __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: @@ -101,7 +102,7 @@ def get_data(self, socket): node_msg = pickle.loads(node_msg) if type(node_msg) == type([]): msg_list.append(node_msg) - if node_msg[0] == WALLET_CHAIN: + if node_msg[0] == WALLET_CHAIN or node_msg[0] == NEW_TX: break if len(msg_list) > 0: return msg_list @@ -149,14 +150,17 @@ def communucate_nodes(self): pickled_chain = pickle.dumps(self.blockchain.chain) data = f"{len(pickled_chain):<{HEADER_LENGTH}}".encode() + pickled_chain node_client.send(data) + if self.blockchain.last_block() != None : print(self.blockchain.last_block().hash) print('chain sent!') 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) - + print(Transaction.from_dict(new_tx).sig) + self.blockchain.create_block(self.wallet.pu_ser.decode()) + print(Blockchain.check_chain(self.blockchain.chain)) + node_client.close() def pop_nodes(self,nodes): @@ -193,12 +197,8 @@ def __connect_nodes(self): self.pop_nodes(excep_nodes) self.blockchain.current_nodes.add((self.IP,self.PORT)) - def new_job(self, target, args = None, daemon = False): - t = threading.Thread(target=target) - # t = Process(target=target) + def new_job(self, target, args = (), daemon = False): + t = threading.Thread(target=target, args = args) self.threads[target] = t - if args != None: - t.args = args t.daemon = daemon t.start() - \ No newline at end of file diff --git a/tcoin_base/transaction.py b/tcoin_base/transaction.py index 6cf522d..493878b 100644 --- a/tcoin_base/transaction.py +++ b/tcoin_base/transaction.py @@ -21,4 +21,14 @@ def gather(self): return [self.input,self.output] def __str__(self): - return f"Sender: {self.sender[30:40].decode()}... \n Receiver: {self.receiver[30:40].decode()}... \n Amount: {self.output} Tx fee: {self.tx_fee}" \ No newline at end of file + return f"Sender: {self.sender[30:40]}... \n Receiver: {self.receiver[30:40]}... \n Amount: {self.output} Tx fee: {self.tx_fee}" + + def dict(self): + dict = {'sender':self.sender,'receiver':self.receiver,'input':self.input,'output':self.output,'tx_fee':self.tx_fee,'sig':self.sig} + return dict + + @staticmethod + 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 diff --git a/tcoin_base/wallet.py b/tcoin_base/wallet.py index 3e24723..9db0e57 100644 --- a/tcoin_base/wallet.py +++ b/tcoin_base/wallet.py @@ -8,14 +8,14 @@ import threading import time import sys -import errno +import errno, json # rsa.key.PrivateKey._save_pkcs1_pem() => saves key like -----BEGIN RSA PRIVATE KEY----- # rsa.key.PrivateKey._load_pkcs1_pem(data) => loads key from like -----BEGIN RSA PRIVATE KEY----- WALLET_CHAIN = 'WALLET_CHAIN' NEW_TX = 'NEW_TX' -HEADER_LENGTH = 100 +HEADER_LENGTH = 1000 -class OneClient(socket.socket): +class WClient(socket.socket): def __init__(self, conn_addr): super().__init__(socket.AF_INET,socket.SOCK_STREAM) @@ -39,7 +39,15 @@ def connect_blockchain(self): length = int(header.decode()) data = self.recv(length) chain = pickle.loads(data) + # with open('s.json','w') as f: + # json.dump(chain.dict(),f,indent=4) check = Blockchain.check_chain(chain) + if len(chain) == 0: + pass + else: + for block in chain: + print(block.hash) + print(len(chain)) if check: return chain else: @@ -53,11 +61,32 @@ def connect_blockchain(self): continue self.close() + @staticmethod + def connect(node_addr): + one_client = WClient(node_addr) + is_connected = one_client.connect_blockchain() + + if is_connected != False and type(is_connected) == type(list()): + print('Connected to Blockchain ! Ready to go !') + chain = is_connected + return chain + else: + print('Try to connect another blockchain node...') + sys.exit() + + @staticmethod + def send_tx(node_addr, tx_data): + one_client = WClient(node_addr) + one_client.send(tx_data) + one_client.close() + class Wallet(socket.socket): def __init__(self, node_addr = tuple(), pr = None, pu = None): # __init__ Wallet self.__BITS = 2048 + self.node_addr = node_addr + print('Connecting to TCOIN BLOCKCHAIN...') # if loaded if pr != None: self.public, self.private = pu, pr @@ -68,39 +97,26 @@ def __init__(self, node_addr = tuple(), pr = None, pu = None): self.pu_ser = rsa.pem.save_pem(self.public._save_pkcs1_pem(),'RSA PUBLIC KEY') - self.chain = None - one_client = OneClient(node_addr) - is_connected = one_client.connect_blockchain() - - if is_connected: - self.chain = is_connected - print('Connected to Blockchain ! Ready to go !') - else: - print('Try to connect another blockchain node...') - sys.exit() - - # __init__ socket - print('Connecting to TCOIN BLOCKCHAIN...') - # creating a client for connecting blockchain - super().__init__(socket.AF_INET,socket.SOCK_STREAM) - if len(node_addr) > 0: - self.connect(node_addr) - print('connected') - self.setblocking(False) + # connect to blockchain if node_addr is given + if len(self.node_addr) != 0: + self.connect_blockchain() + def connect_blockchain(self): + self.chain = WClient.connect(self.node_addr) + def calculate_coins(self): total = 0 - plus = [] - minus = [] + plus = 0 + minus = 0 for block in self.chain: - for tx in block.transactions: - if tx.sender == self.pu_ser: - minus.append(tx.input) - if tx.receiver == self.pu_ser: - plus.append(tx.output) - if tx.miner == self.pu_ser: - plus.append(tx.tx_fee) - total = sum(plus) - sum(minus) + for tx_dict in block.transactions: + tx = Transaction.from_dict(tx_dict) + if tx.sender == self.pu_ser.decode(): + minus += tx.input + if tx.receiver == self.pu_ser.decode(): + plus += tx.output + total = plus - minus + print(plus,minus) return total def sign(self, tx): @@ -113,20 +129,22 @@ def sign(self, tx): def send_tx(self, receiver_pu, amount, tx_fee): new_tx = Transaction( - sender = self.pu_ser, - receiver = receiver_pu, + sender = self.pu_ser.decode(), + receiver = receiver_pu.decode(), input = amount + tx_fee, output = amount ) sig, signed = self.sign(new_tx.gather()) if signed: new_tx.sig = sig + new_tx = new_tx.dict() pickled_tx = pickle.dumps(new_tx) tx_data = [NEW_TX, pickled_tx] tx_data = pickle.dumps(tx_data) data = f"{len(tx_data):<{HEADER_LENGTH}}".encode() + tx_data try: - self.send(data) + # creating a new WClient and sending new tx + WClient.send_tx(self.node_addr, data) print('sending tx...') return True except: @@ -146,39 +164,8 @@ def save_wallet_pem(self, file_path = './'): except: return False - def communucate_blockchain(self): - message = [WALLET_CHAIN] - message = pickle.dumps(message) - message = f"{len(message):<{HEADER_LENGTH}}".encode() + message - self.send(message) - while True: - try: - while True: - # receive things - header = self.recv(HEADER_LENGTH) - if not len(header): - print("Connection closed by the blockchain node") - sys.exit() - length = int(header.decode()) - data = self.recv(length) - chain = pickle.loads(data) - check = Blockchain.check_chain(chain) - if check: - self.chain = chain - return True - else: - return False - except IOError as e: - if e.errno != errno.EAGAIN or e.errno != errno.EWOULDBLOCK: - print('Read Error !',str(e)) - return False - else: - continue - - def new_job(self, target, args = None, daemon = False): - t = threading.Thread(target=target) - if args != None: - t.args = args + def new_job(self, target, args = (), daemon = False): + t = threading.Thread(target=target, args = args) t.daemon = daemon t.start() @@ -193,7 +180,7 @@ def verify(data, sig, public_key): # loads private key from .pem file @staticmethod - def load_wallet_pem(node_addr, file_path = './'): + def load_wallet_pem(node_addr = tuple(), file_path = './'): try: with open(file_path + 'pr_key.pem','rb') as pem_key: private_key = rsa.pem.load_pem(pem_key.read(),'RSA PRIVATE KEY')