diff --git a/bal/__init__.py b/bal/__init__.py index 139597f..e69de29 100644 --- a/bal/__init__.py +++ b/bal/__init__.py @@ -1,2 +0,0 @@ - - diff --git a/bal/balmn.py b/bal/balmn.py index c960e0a..329065d 100644 --- a/bal/balmn.py +++ b/bal/balmn.py @@ -1,21 +1,20 @@ -from mininet.node import ( Host, CPULimitedHost ) +from mininet.node import (Host, CPULimitedHost) from mininet.util import specialClass from mininet.topo import SingleSwitchTopo, LinearTopo, SingleSwitchReversedTopo from mininet.topolib import TreeTopo -from bal.bcnode import ( POWNode, POSNode ) +from bal.bcnode import (POWNode, POSNode) HOSTDEF = 'proc' -HOSTS = { 'proc': Host, - 'rt': specialClass( CPULimitedHost, defaults=dict( sched='rt' ) ), - 'cfs': specialClass( CPULimitedHost, defaults=dict( sched='cfs' ) ), - 'pow': POWNode, - 'pos': POSNode} +HOSTS = {'proc': Host, + 'rt': specialClass(CPULimitedHost, defaults=dict(sched='rt')), + 'cfs': specialClass(CPULimitedHost, defaults=dict(sched='cfs')), + 'pow': POWNode, + 'pos': POSNode} TOPODEF = 'none' -TOPOS = { 'minimal': lambda: SingleSwitchTopo( k=2 ), - 'linear': LinearTopo, - 'reversed': SingleSwitchReversedTopo, - 'single': SingleSwitchTopo, - 'none': None, - 'tree': TreeTopo - } +TOPOS = {'minimal': lambda: SingleSwitchTopo(k=2), + 'linear': LinearTopo, + 'reversed': SingleSwitchReversedTopo, + 'single': SingleSwitchTopo, + 'none': None, + 'tree': TreeTopo} diff --git a/bal/bcnode.py b/bal/bcnode.py index 63fdcdd..4538404 100644 --- a/bal/bcnode.py +++ b/bal/bcnode.py @@ -12,14 +12,15 @@ import os -class BCNode( CPULimitedHost): + +class BCNode(CPULimitedHost): """A BCNode is a Node that is running (or has execed?) an block[chain] application.""" - def __init__( self, name, inNamespace=True, - server='', sargs='', sdir='/tmp/bcn', - client='', cargs='{command}', cdir=None, - ip="127.0.0.1", port='', socket='6000', **params ): + def __init__(self, name, inNamespace=True, + server='', sargs='', sdir='/tmp/bcn', + client='', cargs='{command}', cdir=None, + ip="127.0.0.1", port='', socket='6000', **params): # Server params self.server = server self.sargs = sargs @@ -31,42 +32,39 @@ def __init__( self, name, inNamespace=True, self.ip = ip self.port = port - self.socket= socket - CPULimitedHost.__init__( self, name, inNamespace=inNamespace, - ip=ip, **params ) + self.socket = socket + CPULimitedHost.__init__(self, name, inNamespace=inNamespace, + ip=ip, **params) - def start( self, sim_path ): + def start(self, sim_path): """Start on node. Log to /tmp/bc_.log""" if self.server: - pathCheck( self.server ) - cout = self.sdir + '/bc_' + self.name + '.log' + pathCheck(self.server) + cout = self.sdir + '/bc_' + self.name + '.log' if self.sdir is not None: try: os.stat(self.sdir) - except: + except BaseException: os.mkdir(self.sdir) - self.cmd( 'cd ' + self.sdir ) + self.cmd('cd ' + self.sdir) cmd = self.server if self.sargs: - cmd += " " + self.sargs.format(name=self.name, - IP=self.IP(), - port=self.port, - cdir=self.cdir, - sdir=self.sdir, - socket=self.socket, - simulation_path = sim_path) - debug( cmd + ' 1>' + cout + ' 2>' + cout + ' &' ) - self.cmd( cmd + ' 1>' + cout + ' 2>' + cout + ' &' ) + cmd += " " + self.sargs.format(name=self.name, IP=self.IP(), + port=self.port, cdir=self.cdir, + sdir=self.sdir, socket=self.socket, + simulation_path=sim_path) + debug(cmd + ' 1>' + cout + ' 2>' + cout + ' &') + self.cmd(cmd + ' 1>' + cout + ' 2>' + cout + ' &') self.execed = False - def stop( self, *args, **kwargs ): + def stop(self, *args, **kwargs): "Stop node." - self.cmd( 'kill %' + self.server ) - self.cmd( 'wait %' + self.server ) - super( BCNode, self ).stop( *args, **kwargs ) + self.cmd('kill %' + self.server) + self.cmd('wait %' + self.server) + super(BCNode, self).stop(*args, **kwargs) - def isAvailable( self ): + def isAvailable(self): "Is executables available?" cmd = 'which ' if self.server: @@ -75,66 +73,56 @@ def isAvailable( self ): cmd += self.client return quietRun(cmd) - - def call(self, command, silent= False, data=''): + def call(self, command, silent=False, data=''): """Call on node.""" if self.cdir is not None: - self.cmd( 'cd ' + self.cdir ) + self.cmd('cd ' + self.cdir) cmd = self.client - pathCheck( cmd ) + pathCheck(cmd) if data: - method = '''POST -H "Content-Type: application/json" -d '{data}' '''.format(data = data) + method = '''POST -H "Content-Type: application/json" -d '{data}' '''.format(data=data) else: method = "GET" if self.cargs: - cmd += " " + self.cargs.format(command=command, - method=method, - name=self.name, - IP=self.IP(), - port=self.port, - cdir=self.cdir, + cmd += " " + self.cargs.format(command=command, method=method, + name=self.name, IP=self.IP(), + port=self.port, cdir=self.cdir, sdir=self.sdir) else: - cmd += " " + command + cmd += " " + command if silent: - result = self.cmd( cmd ) + result = self.cmd(cmd) else: - result = self.cmdPrint( cmd ) + result = self.cmdPrint(cmd) debug("command: %s = %s" % (cmd, result)) return result + class POWNode(BCNode): """A POWNode is a BCNode that is running an POWBlockchain.""" - def __init__( self, name, bcclass=None, inNamespace=True, - server='blockchain.py', - sargs='-p {port} -s {socket} -d 2 -k {sdir}/{IP}pow.pem -n {name} -sp {simulation_path}', - sdir='/tmp/bcn', - client='curl', - cargs="-s -X {method} http://{IP}:{port}/{command}", - cdir=None, - ip="127.0.0.1", port='5000', **params ): + def __init__(self, name, bcclass=None, inNamespace=True, server='blockchain.py', + sargs='-p {port} -s {socket} -d 2 -k {sdir}/{IP}pow.pem -n {name} -sp {simulation_path}', + sdir='/tmp/bcn', client='curl', cargs="-s -X {method} http://{IP}:{port}/{command}", + cdir=None, ip="127.0.0.1", port='5000', **params): + + BCNode.__init__(self, name, inNamespace=inNamespace, + server=server, sargs=sargs, sdir=sdir, + client=client, cargs=cargs, cdir=cdir, + ip=ip, port=port, **params) - BCNode.__init__( self, name, inNamespace=inNamespace, - server=server, sargs=sargs, sdir=sdir, - client=client, cargs=cargs, cdir=cdir, - ip=ip, port=port, **params ) class POSNode(BCNode): """A POSNode is a BCNode that is running an POSBlockchain.""" - def __init__( self, name, bcclass=None, inNamespace=True, - server='blockchain.py', - sargs='-p {port} -s {socket} -v pos -k {sdir}/{IP}pos.pem -n {name} -sp {simulation_path}', - sdir='/tmp/bcn', - client='curl', - cargs="-s -X {method} http://{IP}:{port}/{command}", - cdir=None, - ip="127.0.0.1", port='5000', socket='6000', **params ): - - BCNode.__init__( self, name, inNamespace=inNamespace, - server=server, sargs=sargs, sdir=sdir, - client=client, cargs=cargs, cdir=cdir, - ip=ip, port=port, **params ) + def __init__(self, name, bcclass=None, inNamespace=True, server='blockchain.py', + sargs='-p {port} -s {socket} -v pos -k {sdir}/{IP}pos.pem -n {name} -sp {simulation_path}', + sdir='/tmp/bcn', client='curl', cargs="-s -X {method} http://{IP}:{port}/{command}", + cdir=None, ip="127.0.0.1", port='5000', socket='6000', **params): + + BCNode.__init__(self, name, inNamespace=inNamespace, + server=server, sargs=sargs, sdir=sdir, + client=client, cargs=cargs, cdir=cdir, + ip=ip, port=port, **params) diff --git a/bal/blockchain.py b/bal/blockchain.py index eafa12c..166808f 100755 --- a/bal/blockchain.py +++ b/bal/blockchain.py @@ -23,33 +23,40 @@ blockchain = None loop_started = False + @app.route('/transactions/unspenttxouts', methods=['GET']) def do_unspent_tx_outputs(): return jsonify(blockchain.unspent_tx_outs), 200 + @app.route('/transactions/unspenttxouts/my', methods=['GET']) def do_my_unspent_tx_outs(): return jsonify(blockchain.get_my_unspent_transaction_outputs()), 200 + @app.route('/address/my', methods=['GET']) def do_address(): address = get_public_from_wallet() return jsonify({'address': address}), 200 + @app.route('/balance/my', methods=['GET']) def do_get_my_balance(): balance = blockchain.get_my_account_balance() return jsonify({'balance': balance}), 200 + @app.route('/balance/
', methods=['GET']) def do_get_balance(address): balance = blockchain.get_account_balance(address) return jsonify({'balance': balance}), 200 + @app.route('/transactions/pool', methods=['GET']) def do_get_transaction_pool(): return jsonify(blockchain.transaction_pool.get_transaction_pool()), 200 + @app.route('/block/generate', methods=['GET']) def do_generate_block(): new_block = blockchain.generate_next_block() @@ -58,6 +65,7 @@ def do_generate_block(): else: return 'Could not generate new block', 400 + @app.route('/block/generate/loop/start', methods=['GET']) def do_generate_loop_start(): global loop_started @@ -66,13 +74,14 @@ def do_generate_loop_start(): return "Loop has already started.", 200 loop_started = True threading.Thread( - target = do_generate_loop_helper, + target=do_generate_loop_helper, ).start() return "Started Generation Loop (Asynchronous)", 200 except Exception as e: traceback.print_exc() return jsonify(str(e)), 500 + @app.route('/block/generate/loop/stop', methods=['GET']) def do_generate_loop_stop(): global loop_started @@ -83,6 +92,7 @@ def do_generate_loop_stop(): traceback.print_exc() return jsonify(str(e)), 500 + def do_generate_loop_helper(): global loop_started try: @@ -92,14 +102,17 @@ def do_generate_loop_helper(): except Exception: traceback.print_exc() + @app.route('/block/latest', methods=['GET']) def do_latest_block(): return jsonify(blockchain.get_latest_block()), 200 + @app.route('/block/', methods=['GET']) def do_block_index(index): return jsonify(blockchain.get_blockchain()[index]), 200 + @app.route('/transactions/send', methods=['POST']) def do_new_transaction(): values = yaml.safe_load(json.dumps(request.get_json())) @@ -119,28 +132,35 @@ def do_new_transaction(): return jsonify(tx), 200 + @app.route('/transactions/has_amount/', methods=['GET']) def do_has_amount_for_transaction(amount): try: - create_transaction(get_public_from_wallet(), amount, get_private_from_wallet(), blockchain.get_unspent_tx_outs(), blockchain.transaction_pool.get_transaction_pool()) + create_transaction( get_public_from_wallet(), amount, + get_private_from_wallet(), blockchain.get_unspent_tx_outs(), + blockchain.transaction_pool.get_transaction_pool()) return jsonify(True), 200 except Exception as e: traceback.print_exc() tx = str(e) return jsonify(False), 200 + @app.route('/chain', methods=['GET']) def do_full_chain(): return jsonify(blockchain.full_chain()), 200 + @app.route('/chain/length', methods=['GET']) def do_chain_length(): return jsonify(blockchain.full_chain()['length']), 200 + @app.route('/peers', methods=['GET']) def do_get_peers(): return jsonify(blockchain.p2p.get_peers()), 200 + @app.route('/peers/register', methods=['POST']) def do_register_peers(): values = yaml.safe_load(json.dumps(request.get_json()))['peer'] @@ -148,19 +168,44 @@ def do_register_peers(): return 'Missing values', 200 return jsonify(blockchain.p2p.add_peer(values)), 200 + if __name__ == '__main__': from argparse import ArgumentParser parser = ArgumentParser() - parser.add_argument('-p', '--port', default=5000, type=int, help='port to listen on') - parser.add_argument('-s', '--socket', default=6001, type=int, help='p2p port to listen on') - parser.add_argument('-db', '--database', default='', help='db file') - parser.add_argument('-v', '--variant', default='pow', help='variant of blockchain "pow" or "pos"') - parser.add_argument('-d', '--difficulty', default=4, type=int, help='initial difficulty') - parser.add_argument('-k', '--keystore', default='/tmp/private_key.pem', help='where the keystore located. default: private_key.pem') - parser.add_argument('-sp', '--simulationpath', default='', help='specifies if it is a simulation run and where simulation logs will be kept.') - parser.add_argument('-n', '--name', default='bc', help='specifies blockchain node name(mostly for simulations)') - + + help_message = 'port to listen on' + parser.add_argument('-p', '--port', default=5000, type=int, + help=help_message) + + help_message = 'p2p port to listen on' + parser.add_argument('-s', '--socket', default=6001, type=int, + help=help_message) + + help_message = 'db file' + parser.add_argument('-db', '--database', default='', + help=help_message) + + help_message = 'variant of blockchain "pow" or "pos"' + parser.add_argument('-v', '--variant', default='pow', + help=help_message) + + help_message = 'initial difficulty' + parser.add_argument('-d', '--difficulty', default=4, type=int, + help=help_message) + + help_message = 'where the keystore located. default: private_key.pem' + parser.add_argument('-k', '--keystore', default='/tmp/private_key.pem', + help=help_message) + + help_message = 'specifies if it is a simulation run and where simulation logs will be kept.' + parser.add_argument('-sp', '--simulationpath', default='', + help=help_message) + + help_message = 'specifies blockchain node name(mostly for simulations)' + parser.add_argument('-n', '--name', default='bc', + help=help_message) + args = parser.parse_args() port = args.port dbfile = args.database @@ -169,9 +214,11 @@ def do_register_peers(): simulation_path = args.simulationpath if simulation_path != '': if args.variant.find('pos') == 0: - blockchain = POSBlockchainSimulation(p2p_port, initial_difficulty, simulation_path, args.name) + blockchain = POSBlockchainSimulation(p2p_port, initial_difficulty, + simulation_path, args.name) else: - blockchain = POWBlockchainSimulation(p2p_port, initial_difficulty, simulation_path, args.name) + blockchain = POWBlockchainSimulation(p2p_port, initial_difficulty, + simulation_path, args.name) else: if args.variant.find('pos') == 0: blockchain = POSBlockchain(p2p_port, initial_difficulty) @@ -182,8 +229,5 @@ def do_register_peers(): print("DB: " + dbfile) blockchain.init_db(dbfile) init_wallet(args.keystore) - threading.Thread( - target = blockchain.p2p.start, - args = () - ).start() + threading.Thread(target=blockchain.p2p.start, args=()).start() app.run(host='0.0.0.0', port=port, threaded=True) diff --git a/bal/p2p.py b/bal/p2p.py index 39b401f..a44257e 100644 --- a/bal/p2p.py +++ b/bal/p2p.py @@ -1,10 +1,13 @@ -import socket, pickle, threading +import socket +import pickle +import threading from enum import Enum from time import sleep import json import struct import traceback + class MessageType(Enum): QUERY_LATEST_BLOCK = 0 QUERY_ALL = 1 @@ -12,12 +15,14 @@ class MessageType(Enum): QUERY_TRANSACTION_POOL = 3 RESPONSE_TRANSACTION_POOL = 4 + class Message: def __init__(self, message_type, message_data, reply_addr): self.type = message_type self.data = message_data self.reply_addr = reply_addr + class P2P: def __init__(self, node, socket): self.p2p_addr = ('', socket) @@ -32,19 +37,19 @@ def transaction_pool(self): return self.node.transaction_pool def query(self, peer_addr, message): - threading.Thread( - target = self.send_message, - args = (peer_addr, message) - ).start() + threading.Thread(target=self.send_message, + args=(peer_addr, message)).start() def add_peer(self, peer_str): peer_addr = self.get_peer_tuple(peer_str) if peer_str in self.peer_sockets.values(): return False else: - self.query(peer_addr, Message(MessageType.QUERY_LATEST_BLOCK, '', self.p2p_addr)) + query_message = Message(MessageType.QUERY_LATEST_BLOCK, '', self.p2p_addr) + self.query(peer_addr, query_message) sleep(0.5) - self.query(peer_addr, Message(MessageType.QUERY_TRANSACTION_POOL, '', self.p2p_addr)) + query_message = Message(MessageType.QUERY_TRANSACTION_POOL, '', self.p2p_addr) + self.query(peer_addr, query_message) return True def get_peers(self): @@ -55,21 +60,27 @@ def broadcast_latest(self): will request the entire chain if needed""" for peer_str in self.peer_sockets: peer_addr = self.get_peer_tuple(peer_str) - self.query(peer_addr, Message(MessageType.RESPONSE_BLOCKCHAIN, [self.blockchain().get_latest_block()], self.p2p_addr)) + query_message = Message(MessageType.RESPONSE_BLOCKCHAIN, + [self.blockchain().get_latest_block()], + self.p2p_addr) + self.query(peer_addr, query_message) def broadcast_transaction_pool(self): """Broadcasts the latest block in the chain to connected peers, which will request the entire chain if needed""" for peer_str in self.peer_sockets: peer_addr = self.get_peer_tuple(peer_str) - self.query(peer_addr, Message(MessageType.RESPONSE_TRANSACTION_POOL, self.transaction_pool().get_transaction_pool(), self.p2p_addr)) + query_message = Message(MessageType.RESPONSE_TRANSACTION_POOL, + self.transaction_pool().get_transaction_pool(), + self.p2p_addr) + self.query(peer_addr, query_message) def send_message(self, peer_addr, data): """Sends a message with provided data to a given address, opening a new p2p socket if neccesary""" try: peer_str = self.get_peer_str(peer_addr) - if not peer_str in self.peer_sockets or True: + if peer_str not in self.peer_sockets or True: peer_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) peer_socket.connect(peer_addr) self.peer_sockets[peer_str] = peer_socket @@ -105,7 +116,8 @@ def process_response_chain(self, message): print('Received one block from {}'.format(message.reply_addr[1])) self.broadcast_latest() elif len(received_chain) == 1: - self.query(message.reply_addr, Message(MessageType.QUERY_ALL, '', self.p2p_addr)) + query_message = Message(MessageType.QUERY_ALL, '', self.p2p_addr) + self.query(message.reply_addr, query_message) print('Chain far behind {}, requesting entire chain'.format(message.reply_addr[1])) elif self.blockchain().replace_chain(received_chain): print('Received updated chain from {}'.format(message.reply_addr[1])) @@ -114,7 +126,7 @@ def process_response_chain(self, message): print('Received chain from {} not longer than current chain'.format(message.reply_addr[1])) def get_peer_str(self, peer_tuple): - return ':'.join(map(str,peer_tuple)) + return ':'.join(map(str, peer_tuple)) def get_peer_tuple(self, peer_str): vals = peer_str.split(':') @@ -156,20 +168,30 @@ def start(self): peer_addr = (addr[0], message.reply_addr[1]) message.reply_addr = peer_addr peer_str = self.get_peer_str(peer_addr) - if not peer_str in self.peer_sockets: - self.query(peer_addr, Message(MessageType.QUERY_LATEST_BLOCK, '', self.p2p_addr)) + if peer_str not in self.peer_sockets: + query_message = Message(MessageType.QUERY_LATEST_BLOCK, '', self.p2p_addr) + self.query(peer_addr, query_message) print('Added new peer {}'.format(peer_str)) if message.type == MessageType.RESPONSE_BLOCKCHAIN: self.process_response_chain(message) elif message.type == MessageType.QUERY_ALL: print('Dispatching all blocks to {}'.format(message.reply_addr[1])) - self.query(peer_addr, Message(MessageType.RESPONSE_BLOCKCHAIN, self.blockchain().get_blockchain(), self.p2p_addr)) + query_message = Message(MessageType.RESPONSE_BLOCKCHAIN, + self.blockchain().get_blockchain(), + self.p2p_addr) + self.query(peer_addr, query_message) elif message.type == MessageType.QUERY_LATEST_BLOCK: print('Dispatching latest block to {}'.format(message.reply_addr[1])) - self.query(peer_addr, Message(MessageType.RESPONSE_BLOCKCHAIN, [self.blockchain().get_latest_block()], self.p2p_addr)) + query_message = Message(MessageType.RESPONSE_BLOCKCHAIN, + [self.blockchain().get_latest_block()], + self.p2p_addr) + self.query(peer_addr, query_message) elif message.type == MessageType.QUERY_TRANSACTION_POOL: - self.query(peer_addr, Message(MessageType.RESPONSE_TRANSACTION_POOL, self.transaction_pool().get_transaction_pool(), self.p2p_addr)) + query_message = Message(MessageType.RESPONSE_TRANSACTION_POOL, + self.transaction_pool().get_transaction_pool(), + self.p2p_addr) + self.query(peer_addr, query_message) elif message.type == MessageType.RESPONSE_TRANSACTION_POOL: received_transactions = message.data if not received_transactions: diff --git a/bal/transaction.py b/bal/transaction.py index 4b75c86..b5bba8a 100644 --- a/bal/transaction.py +++ b/bal/transaction.py @@ -6,6 +6,7 @@ import hashlib COINBASE_AMOUNT = 1 + def new_unspent_tx_out(tx_out_id, tx_out_index, address, amount): result = {} result['tx_out_id'] = tx_out_id @@ -14,19 +15,22 @@ def new_unspent_tx_out(tx_out_id, tx_out_index, address, amount): result['amount'] = amount return result -def new_tx_in(tx_out_id, tx_out_index, signature) : + +def new_tx_in(tx_out_id, tx_out_index, signature): result = {} result['tx_out_id'] = tx_out_id result['tx_out_index'] = tx_out_index result['signature'] = signature return result + def new_tx_out(address, amount): result = {} result['address'] = address result['amount'] = amount return result + def new_transaction(id, tx_ins, tx_outs): result = {} result['id'] = id @@ -34,12 +38,16 @@ def new_transaction(id, tx_ins, tx_outs): result['tx_outs'] = tx_outs return result + def get_transaction_id(transaction): - tx_in_content = ''.join(map(lambda tx_in : tx_in['tx_out_id'] + str(tx_in['tx_out_index']), transaction['tx_ins'])) - tx_out_content = ''.join(map(lambda tx_out : tx_out['address'] + str(tx_out['amount']), transaction['tx_outs'])) + tx_in_content = ''.join(map(lambda tx_in: tx_in['tx_out_id'] + str(tx_in['tx_out_index']), + transaction['tx_ins'])) + tx_out_content = ''.join(map(lambda tx_out: tx_out['address'] + str(tx_out['amount']), + transaction['tx_outs'])) encoded = '{}{}'.format(tx_in_content, tx_out_content).encode() return hashlib.sha256(encoded).hexdigest() + def validate_transaction(transaction, a_unspent_tx_outs): if not is_valid_transaction_structure(transaction): return False @@ -58,15 +66,14 @@ def validate_transaction(transaction, a_unspent_tx_outs): return True + def validate_block_transactions(a_transactions, a_unspent_tx_outs, block_index): coinbase_tx = a_transactions[0] if not validate_coinbase_tx(coinbase_tx, block_index): print('invalid coinbase transaction: ' + json.dumps(coinbase_tx)) return False - tx_ins = seq(a_transactions)\ - .map(lambda tx : tx['tx_ins'])\ - .flatten() + tx_ins = seq(a_transactions).map(lambda tx: tx['tx_ins']).flatten() if has_duplicates(tx_ins): print('has duplicates') @@ -75,6 +82,7 @@ def validate_block_transactions(a_transactions, a_unspent_tx_outs, block_index): normal_transactions = a_transactions[1:] return all(validate_transaction(tx, a_unspent_tx_outs) for tx in normal_transactions) + def validate_coinbase_tx(transaction, block_index): if not transaction: print('the first transaction in the block must be coinbase transaction') @@ -102,8 +110,11 @@ def validate_coinbase_tx(transaction, block_index): return True + def validate_tx_in(tx_in, transaction, a_unspent_tx_outs): - referenced_u_tx_out = find_unspent_tx_out(tx_in['tx_out_id'], tx_in['tx_out_index'], a_unspent_tx_outs) + referenced_u_tx_out = find_unspent_tx_out(tx_in['tx_out_id'], + tx_in['tx_out_index'], + a_unspent_tx_outs) if not referenced_u_tx_out: print('referenced txOut not found: ' + json.dumps(tx_in)) @@ -115,11 +126,13 @@ def validate_tx_in(tx_in, transaction, a_unspent_tx_outs): signature = tx_in['signature'].decode("hex") valid_signature = key.verify(signature, transaction['id'].encode()) if not valid_signature: - print('invalid txIn signature: %s txId: %s address: %s', tx_in['signature'], transaction['id'], referenced_u_tx_out['address']) + print('invalid txIn signature: %s txId: %s address: %s', + tx_in['signature'], transaction['id'], referenced_u_tx_out['address']) return False return True + def new_coinbase_transaction(address, block_index): tx_in = new_tx_in('', block_index, '') @@ -127,27 +140,34 @@ def new_coinbase_transaction(address, block_index): t['id'] = get_transaction_id(t) return t + def has_duplicates(tx_ins): - tx_in_values = list(map(lambda tx_in : tx_in['tx_out_id'] + str(tx_in['tx_out_index']), tx_ins)) + tx_in_values = list(map(lambda tx_in: tx_in['tx_out_id'] + str(tx_in['tx_out_index']), tx_ins)) return len(set(tx_in_values)) != len(tx_in_values) + def sign_tx_in(transaction, tx_in_index, private_key, unspent_tx_outs): tx_in = transaction['tx_ins'][tx_in_index] tx_to_sign = transaction['id'] - referenced_unspent_tx_out = find_unspent_tx_out(tx_in['tx_out_id'], tx_in['tx_out_index'], unspent_tx_outs) + referenced_unspent_tx_out = find_unspent_tx_out(tx_in['tx_out_id'], + tx_in['tx_out_index'], + unspent_tx_outs) if not referenced_unspent_tx_out: - raise Exception('could not find referenced txOut') + exception_message = 'could not find referenced txOut' + raise Exception(exception_message) referenced_address = referenced_unspent_tx_out['address'] signing_key = SigningKey.from_der(private_key.decode('hex')) if signing_key.get_verifying_key().to_der().encode('hex') != referenced_address: - raise Exception('trying to sign an input with private' + - ' key that does not match the address that is referenced in txIn') + exception_message = ('trying to sign an input with private key that' + + ' does not match the address that is referenced in txIn') + raise Exception(exception_message) signature = signing_key.sign(tx_to_sign.encode()).encode('hex') return signature + def update_unspent_tx_outs(a_transactions, a_unspent_tx_outs): new_unspent_tx_outs = get_new_unspent_tx_outs(a_transactions) consumed_tx_outs = get_consumed_tx_outs(a_transactions) @@ -155,6 +175,7 @@ def update_unspent_tx_outs(a_transactions, a_unspent_tx_outs): resulting_unspent_tx_outs = get_resulting_unspent_tx_outs(a_unspent_tx_outs, consumed_tx_outs) return resulting_unspent_tx_outs + new_unspent_tx_outs + def process_transactions(a_transactions, a_unspent_tx_outs, block_index): if not validate_block_transactions(a_transactions, a_unspent_tx_outs, block_index): print('invalid block transactions') @@ -162,14 +183,15 @@ def process_transactions(a_transactions, a_unspent_tx_outs, block_index): return update_unspent_tx_outs(a_transactions, a_unspent_tx_outs) + def is_valid_tx_in_structure(tx_in): if not tx_in: print('txIn is null') return False - elif type(tx_in['signature']) != str: + elif not isinstance(tx_in['signature'], str): print('invalid signature type in txIn') return False - elif type(tx_in['tx_out_id']) != str: + elif not isinstance(tx_in['tx_out_id'], str): print('invalid txOutId type in txIn') return False elif not isinstance(tx_in['tx_out_index'], numbers.Number): @@ -178,12 +200,13 @@ def is_valid_tx_in_structure(tx_in): else: return True + def is_valid_tx_out_structure(tx_out): if not tx_out: print('txOut is null') return False - if type(tx_out['address']) != str: + if not isinstance(tx_out['address'], str): print('invalid address type in txOut') return False @@ -197,8 +220,9 @@ def is_valid_tx_out_structure(tx_out): return True + def is_valid_transaction_structure(transaction): - if type(transaction['id']) != str: + if not isinstance(transaction['id'], str): print('transactionId missing') return False @@ -218,6 +242,7 @@ def is_valid_transaction_structure(transaction): return True + def is_valid_address(address): if len(address) != 176: print(address) @@ -228,38 +253,47 @@ def is_valid_address(address): return False return True + def get_new_unspent_tx_outs(new_transactions): result = [] for transaction in new_transactions: tx_outs = transaction['tx_outs'] - tx_objs = [new_unspent_tx_out(transaction['id'], index, tx_out['address'], tx_out['amount']) for index, tx_out in enumerate(tx_outs)] + tx_objs = [new_unspent_tx_out(transaction['id'], index, tx_out['address'], tx_out['amount']) for index, + tx_out in enumerate(tx_outs)] result.extend(tx_objs) return result + def get_consumed_tx_outs(new_transactions): - return seq(new_transactions)\ - .map(lambda t : t['tx_ins'])\ - .reduce(lambda a, b : a + b, [])\ - .map(lambda tx_in : new_unspent_tx_out(tx_in['tx_out_id'], tx_in['tx_out_index'], '',0)) + return (seq(new_transactions) + .map(lambda t: t['tx_ins']) + .reduce(lambda a, b: a +b, []) + .map(lambda tx_in: new_unspent_tx_out(tx_in['tx_out_id'], tx_in['tx_out_index'], '', 0))) + def get_resulting_unspent_tx_outs(unspent_tx_outs, consumed_tx_outs): - return [tx for tx in unspent_tx_outs or [] if not find_unspent_tx_out(tx['tx_out_id'], tx['tx_out_index'], consumed_tx_outs)] + return [tx for tx in unspent_tx_outs or [] + if not find_unspent_tx_out(tx['tx_out_id'], tx['tx_out_index'], consumed_tx_outs)] + def has_valid_tx_ins(transaction, a_unspent_tx_outs): - return seq(transaction['tx_ins'])\ - .map(lambda tx_in : validate_tx_in(tx_in, transaction, a_unspent_tx_outs))\ - .reduce(lambda a, b : a and b, True) + return (seq(transaction['tx_ins']) + .map(lambda tx_in: validate_tx_in(tx_in, transaction, a_unspent_tx_outs)) + .reduce(lambda a, b: a and b, True)) + def total_tx_in_values(transaction, a_unspent_tx_outs): - return seq(transaction['tx_ins'])\ - .map(lambda tx_in : get_tx_in_amount(tx_in, a_unspent_tx_outs))\ - .reduce(lambda a, b : (a + b), 0) + return (seq(transaction['tx_ins']) + .map(lambda tx_in: get_tx_in_amount(tx_in, a_unspent_tx_outs)) + .reduce(lambda a, b: (a + b), 0)) + def total_tx_out_values(transaction): - return seq(transaction['tx_outs'])\ - .map(lambda tx_out : tx_out['amount'])\ - .reduce(lambda a, b : (a + b), 0) + return (seq(transaction['tx_outs']) + .map(lambda tx_out: tx_out['amount']) + .reduce(lambda a, b: (a + b), 0)) + def find_unspent_tx_out(transaction_id, index, a_unspent_tx_outs): for u_tx_o in a_unspent_tx_outs: @@ -267,5 +301,8 @@ def find_unspent_tx_out(transaction_id, index, a_unspent_tx_outs): return u_tx_o return None + def get_tx_in_amount(tx_in, a_unspent_tx_outs): - return find_unspent_tx_out(tx_in['tx_out_id'], tx_in['tx_out_index'], a_unspent_tx_outs)['amount'] + return find_unspent_tx_out(tx_in['tx_out_id'], + tx_in['tx_out_index'], + a_unspent_tx_outs)['amount'] diff --git a/bal/transaction_pool.py b/bal/transaction_pool.py index 84e65d2..4240a6c 100644 --- a/bal/transaction_pool.py +++ b/bal/transaction_pool.py @@ -3,6 +3,7 @@ import json from functional import seq + class TransactionPool: def __init__(self): self.transaction_pool = [] @@ -12,16 +13,20 @@ def get_transaction_pool(self): def add_to_transaction_pool(self, tx, unspent_tx_outs): if not validate_transaction(tx, unspent_tx_outs): - raise Exception('Trying to add invalid tx to pool: ' + json.dumps(tx)) + exception_message = 'Trying to add invalid tx to pool: ' + json.dumps(tx) + raise Exception(exception_message) if not self.is_valid_tx_for_pool(tx): - raise Exception('Trying to add same tx to pool') + exception_message = 'Trying to add same tx to pool' + raise Exception(exception_message) print('adding to txPool: %s', json.dumps(tx)) self.transaction_pool.append(tx.copy()) def has_tx_in(self, tx_in, unspent_tx_outs): - found_tx_in = find_unspent_tx_out(tx_in['tx_out_id'], tx_in['tx_out_index'], unspent_tx_outs) + found_tx_in = find_unspent_tx_out(tx_in['tx_out_id'], + tx_in['tx_out_index'], + unspent_tx_outs) return bool(found_tx_in) def update_transaction_pool(self, unspent_tx_outs): @@ -32,13 +37,14 @@ def update_transaction_pool(self, unspent_tx_outs): invalid_txs.append(tx) break if len(invalid_txs) > 0: - print('removing the following transactions from txPool: %s', json.dumps(invalid_txs)) + print('removing the following transactions from txPool: %s', + json.dumps(invalid_txs)) self.transaction_pool = [tx for tx in self.transaction_pool if tx not in invalid_txs] def get_tx_pool_ins(self): - return seq(self.transaction_pool)\ - .map(lambda tx : tx['tx_ins'])\ - .flatten() + return (seq(self.transaction_pool) + .map(lambda tx: tx['tx_ins']) + .flatten()) def is_valid_tx_for_pool(self, tx): tx_pool_ins = self.get_tx_pool_ins() diff --git a/bal/variant/base_blockchain.py b/bal/variant/base_blockchain.py index b7b420d..9b6065a 100644 --- a/bal/variant/base_blockchain.py +++ b/bal/variant/base_blockchain.py @@ -14,9 +14,10 @@ from bal.transaction_pool import TransactionPool from bal.wallet import create_transaction, find_unspent_tx_outs, get_balance, get_private_from_wallet, get_public_from_wallet -DIFFICULTY_ADJUSTMENT_INTERVAL = 16 # block number -BLOCK_GENERATION_INTERVAL = 2 # in seconds -VALID_TIMESTAMP_INTERVAL = 60 # in seconds +DIFFICULTY_ADJUSTMENT_INTERVAL = 16 # block number +BLOCK_GENERATION_INTERVAL = 2 # in seconds +VALID_TIMESTAMP_INTERVAL = 60 # in seconds + class BaseBlockchain(object): def __init__(self, p2p_port, initial_difficulty): @@ -55,10 +56,12 @@ def genesis_transaction(self): @abc.abstractmethod def find_block(self, index, previous_hash, transactions, difficulty): - return NotImplemented + return NotImplemented def genesis_block(self): - return self.raw_block(0, 1465154705, '', [self.genesis_transaction()], self.get_initial_difficulty()) + return self.raw_block(0, 1465154705, '', + [self.genesis_transaction()], + self.get_initial_difficulty()) def raw_block(self, index, timestamp, previous_hash, transactions, proof): """ @@ -96,8 +99,8 @@ def get_adjusted_difficulty(self, latest_block, a_block_chain): return prev_adjustment_block['difficulty'] def is_valid_timestamp(self, block, previous_block): - result = (previous_block['timestamp'] - VALID_TIMESTAMP_INTERVAL < block['timestamp']) and \ - block['timestamp'] - VALID_TIMESTAMP_INTERVAL < time() + result = ((previous_block['timestamp'] - VALID_TIMESTAMP_INTERVAL < block['timestamp']) + and block['timestamp'] - VALID_TIMESTAMP_INTERVAL < time()) return result def generate_raw_next_block(self, transactions): @@ -105,7 +108,8 @@ def generate_raw_next_block(self, transactions): previous_block = self.get_latest_block() difficulty = self.get_difficulty(self.get_blockchain()) next_index = previous_block['index'] + 1 - new_block = self.find_block(next_index, previous_block['hash'], transactions, difficulty) + new_block = self.find_block(next_index,previous_block['hash'], + transactions,difficulty) if self.add_block_to_chain(new_block): self.after_generate_raw_next_block(new_block) self.p2p.broadcast_latest() @@ -120,10 +124,12 @@ def after_generate_raw_next_block(self, new_block): pass def get_my_unspent_transaction_outputs(self): - return find_unspent_tx_outs(get_public_from_wallet(), self.get_unspent_tx_outs()) + return find_unspent_tx_outs(get_public_from_wallet(), + self.get_unspent_tx_outs()) def generate_next_block(self): - coinbase_tx = new_coinbase_transaction(get_public_from_wallet(), self.get_latest_block()['index'] + 1) + coinbase_tx = new_coinbase_transaction(get_public_from_wallet(), + self.get_latest_block()['index'] + 1) transactions = [coinbase_tx] + self.transaction_pool.get_transaction_pool() return self.generate_raw_next_block(transactions) @@ -134,26 +140,30 @@ def get_account_balance(self, address): return get_balance(address, self.get_unspent_tx_outs()) def send_transaction(self, address, amount): - tx = create_transaction(address, amount, get_private_from_wallet(), self.get_unspent_tx_outs(), self.transaction_pool.get_transaction_pool()) + tx = create_transaction(address, + amount, + get_private_from_wallet(), + self.get_unspent_tx_outs(), + self.transaction_pool.get_transaction_pool()) self.before_send_transaction(tx) self.transaction_pool.add_to_transaction_pool(tx, self.get_unspent_tx_outs()) self.p2p.broadcast_transaction_pool() self.after_send_transaction(tx) return tx - def before_send_transaction(self,tx): + def before_send_transaction(self, tx): pass - def after_send_transaction(self,tx): + def after_send_transaction(self, tx): pass def is_valid_block_structure(self, block): - return isinstance(block['index'], numbers.Number) and \ - type(block['hash']) == str and \ - type(block['previous_hash']) == str and \ - isinstance(block['timestamp'], numbers.Number) and \ - type(block['transactions']) == list and \ - isinstance(block['difficulty'], numbers.Number) + return (isinstance(block['index'], numbers.Number) + and isinstance(block['hash'], str) + and isinstance(block['previous_hash'], str) + and isinstance(block['timestamp'], numbers.Number) + and isinstance(block['transactions'], list) + and isinstance(block['difficulty'], numbers.Number)) def has_valid_hash(self, block): block_content = {x: block[x] for x in block if x != 'hash'} @@ -168,10 +178,10 @@ def handle_received_transaction(self, transaction): self.transaction_pool.add_to_transaction_pool(transaction, self.get_unspent_tx_outs()) self.after_handle_received_transaction(transaction) - def before_handle_received_transaction(self,transaction): + def before_handle_received_transaction(self, transaction): pass - def after_handle_received_transaction(self,transaction): + def after_handle_received_transaction(self, transaction): pass @staticmethod @@ -181,7 +191,8 @@ def hash(block): :param block: Block """ - # We must make sure that the Dictionary is Ordered, or we'll have inconsistent hashes + # We must make sure that the Dictionary is Ordered, or we'll have + # inconsistent hashes block_string = json.dumps(block, sort_keys=True).encode() return hashlib.sha256(block_string).hexdigest() @@ -205,7 +216,6 @@ def valid_chain(self, chain): return True def is_valid_block(self, block, previous_block): - """ :param last_hash: The hash of the Previous Block :return: True if correct, False if not. @@ -213,7 +223,7 @@ def is_valid_block(self, block, previous_block): if not self.is_valid_block_structure(block): print('invalid block structure', json.dumps(block)) return False - if previous_block['index'] + 1 != block['index']: + if previous_block['index'] + 1 != block['index']: return False elif previous_block['hash'] != block['previous_hash']: return False @@ -227,14 +237,15 @@ def full_chain(self): response = { 'chain': self.chain, 'length': len(self.chain) - } return response def add_block_to_chain(self, new_block): if self.is_valid_block(new_block, self.get_latest_block()): with self.lock: - ret_val = process_transactions(new_block['transactions'], self.get_unspent_tx_outs(), new_block['index']) + ret_val = process_transactions(new_block['transactions'], + self.get_unspent_tx_outs(), + new_block['index']) if ret_val: self.before_update_chain(new_block) self.chain.append(new_block) @@ -255,7 +266,9 @@ def replace_chain(self, chain): a_unspent_tx_outs = [] for block in chain: self.before_update_chain(block) - a_unspent_tx_outs = process_transactions(block['transactions'], a_unspent_tx_outs, block['index']) + a_unspent_tx_outs = process_transactions(block['transactions'], + a_unspent_tx_outs, + block['index']) self.unspent_tx_outs = a_unspent_tx_outs self.transaction_pool.update_transaction_pool(a_unspent_tx_outs) self.after_update_chain(block) @@ -264,17 +277,17 @@ def replace_chain(self, chain): print('Received blockchain invalid') return False - def before_update_chain(self,block): + def before_update_chain(self, block): pass - def after_update_chain(self,block): + def after_update_chain(self, block): pass def get_accumulated_difficulty(self, a_blockchain): - return seq(a_blockchain)\ - .map(lambda block : block['difficulty'])\ - .map(lambda difficulty : 2 ** difficulty)\ - .reduce(lambda a, b : a + b) + return (seq(a_blockchain) + .map(lambda block: block['difficulty']) + .map(lambda difficulty: 2 ** difficulty) + .reduce(lambda a, b: a + b)) def save_db(self): if self.db is not None: @@ -289,6 +302,6 @@ def init_db(self, dbfile): if db['chain']: self.chain = yaml.safe_load(json.dumps(json.loads(db['chain']))) - except: + except BaseException: db.close() self.save_db() diff --git a/bal/variant/pos_blockchain.py b/bal/variant/pos_blockchain.py index 21b4449..4a99996 100644 --- a/bal/variant/pos_blockchain.py +++ b/bal/variant/pos_blockchain.py @@ -8,11 +8,14 @@ VALIDATING_WITHOUT_COIN = 10 NO_STAKE_HELP_RATE = 10.0 + class POSBlockchain(BaseBlockchain): def genesis_block(self): - return self.raw_block(0, 1465154705, '', [self.genesis_transaction()], self.get_initial_difficulty(), 0, '0001') + return self.raw_block(0, 1465154705, '', [self.genesis_transaction()], + self.get_initial_difficulty(), 0, '0001') - def raw_block(self, index, timestamp, previous_hash, transactions, difficulty, staker_balance, staker_address): + def raw_block(self, index, timestamp, previous_hash, transactions, + difficulty, staker_balance, staker_address): """ Create a new Block in the Blockchain :param previous_hash: Hash of previous Block @@ -42,8 +45,7 @@ def is_block_staking_valid(self, block): stake_helper = COINBASE_AMOUNT / NO_STAKE_HELP_RATE balance = stake_helper - - balance_over_difficulty = (2**256) * balance/(difficulty * 1.0) + balance_over_difficulty = (2**256) * balance / (difficulty * 1.0) previous_hash = block['previous_hash'] staker_address = block['staker_address'] timestamp = block['timestamp'] @@ -60,20 +62,23 @@ def find_block(self, index, previous_hash, transactions, difficulty): while (True): timestamp = time() if previous_time_stamp != timestamp: - temp_block = self.raw_block(index, timestamp, previous_hash, transactions, difficulty, self.get_my_account_balance(), get_public_from_wallet()) + temp_block = self.raw_block(index, timestamp, + previous_hash, transactions, + difficulty, self.get_my_account_balance(), + get_public_from_wallet()) if self.is_block_staking_valid(temp_block): return temp_block previous_time_stamp = timestamp def is_valid_block_structure(self, block): return isinstance(block['index'], numbers.Number) and \ - type(block['hash']) == str and \ - type(block['previous_hash']) == str and \ - isinstance(block['timestamp'], numbers.Number) and \ - type(block['transactions']) == list and \ - isinstance(block['difficulty'], numbers.Number) and \ - isinstance(block['staker_balance'], numbers.Number) and \ - type(block['staker_address']) == str + isinstance(block['hash'], str) and \ + isinstance(block['previous_hash'], str) and \ + isinstance(block['timestamp'], numbers.Number) and \ + isinstance(block['transactions'], list) and \ + isinstance(block['difficulty'], numbers.Number) and \ + isinstance(block['staker_balance'], numbers.Number) and \ + isinstance(block['staker_address'], str) def has_valid_hash(self, block): block_content = {x: block[x] for x in block if x != 'hash'} diff --git a/bal/variant/pos_blockchain_simulation.py b/bal/variant/pos_blockchain_simulation.py index 6152404..ab93cdd 100644 --- a/bal/variant/pos_blockchain_simulation.py +++ b/bal/variant/pos_blockchain_simulation.py @@ -1,25 +1,25 @@ from bal.variant.pos_blockchain import POSBlockchain from time import time + class POSBlockchainSimulation(POSBlockchain): def __init__(self, p2p_port, initial_difficulty, simulation_path, name): super(POSBlockchainSimulation, self).__init__(p2p_port, initial_difficulty) self.path = simulation_path self.name = name - def before_send_transaction(self, tx): ts = time() tx_id = tx['id'] - with open(self.path + 'transaction_pool-'+tx_id+'.txt', 'w') as file: - file.write(self.name + '---'+ 'sending tx' + '---' + str(ts)) + with open(self.path + 'transaction_pool-' + tx_id + '.txt', 'w') as file: + file.write(self.name + '---' + 'sending tx' + '---' + str(ts)) file.write('\n') def before_handle_received_transaction(self, tx): ts = time() tx_id = tx['id'] - with open(self.path + 'transaction_pool-'+tx_id+'.txt', 'a+') as file: - file.write(self.name + '---'+ 'received tx' + '---' + str(ts)) + with open(self.path + 'transaction_pool-' + tx_id + '.txt', 'a+') as file: + file.write(self.name + '---' + 'received tx' + '---' + str(ts)) file.write('\n') def after_generate_raw_next_block(self, block): @@ -28,15 +28,15 @@ def after_generate_raw_next_block(self, block): if len(transactions) > 1: for tx in transactions[1:]: tx_id = tx['id'] - with open(self.path + 'transaction_block-'+tx_id+'.txt', 'w') as file: - file.write(self.name + '---'+ 'sending tx with block' + '---' + str(ts)) + with open(self.path + 'transaction_block-' + tx_id + '.txt', 'w') as file: + file.write(self.name + '---' + 'sending tx with block' + '---' + str(ts)) file.write('\n') def before_update_chain(self, block): ts = time() transactions = block['transactions'] - for tx in transactions[1:]: #ignore coinbase transaction + for tx in transactions[1:]: # ignore coinbase transaction tx_id = tx['id'] - with open(self.path + 'transaction_block-'+tx_id+'.txt', 'a+') as file: - file.write(self.name + '---'+ 'received tx with block' + '---' + str(ts)) + with open(self.path + 'transaction_block-' + tx_id + '.txt', 'a+') as file: + file.write(self.name +'---' + 'received tx with block' + '---' + str(ts)) file.write('\n') diff --git a/bal/variant/pow_blockchain.py b/bal/variant/pow_blockchain.py index 99137e6..5a2ebcd 100644 --- a/bal/variant/pow_blockchain.py +++ b/bal/variant/pow_blockchain.py @@ -2,12 +2,16 @@ import numbers from time import time + class POWBlockchain(BaseBlockchain): def genesis_block(self): - return self.raw_block(0, 1465154705, '', [self.genesis_transaction()], self.get_initial_difficulty(), 0) - + return self.raw_block(0, 1465154705, '', + [self.genesis_transaction()], + self.get_initial_difficulty(), 0) - def raw_block(self, index, timestamp, previous_hash, transactions, difficulty, proof): + def raw_block(self, index, timestamp, + previous_hash, transactions, + difficulty, proof): """ Create a new Block in the Blockchain :param previous_hash: Hash of previous Block @@ -34,19 +38,20 @@ def find_block(self, index, previous_hash, transactions, difficulty): nonce = 0 timestamp = time() while (True): - temp_block = self.raw_block(index, timestamp, previous_hash, transactions, difficulty, nonce) + temp_block = self.raw_block(index, timestamp, previous_hash, + transactions, difficulty, nonce) if self.is_block_proof_valid(temp_block): return temp_block nonce += 1 def is_valid_block_structure(self, block): - return isinstance(block['index'], numbers.Number) and \ - type(block['hash']) == str and \ - type(block['previous_hash']) == str and \ - isinstance(block['timestamp'], numbers.Number) and \ - type(block['transactions']) == list and \ - isinstance(block['difficulty'], numbers.Number) and \ - isinstance(block['proof'], numbers.Number) + return (isinstance(block['index'], numbers.Number) + and isinstance(block['hash'], str) + and isinstance(block['previous_hash'], str) + and isinstance(block['timestamp'], numbers.Number) + and isinstance(block['transactions'], list) + and isinstance(block['difficulty'], numbers.Number) + and isinstance(block['proof'], numbers.Number)) def has_valid_hash(self, block): block_content = {x: block[x] for x in block if x != 'hash'} diff --git a/bal/variant/pow_blockchain_simulation.py b/bal/variant/pow_blockchain_simulation.py index 97be359..e1b71c3 100644 --- a/bal/variant/pow_blockchain_simulation.py +++ b/bal/variant/pow_blockchain_simulation.py @@ -1,6 +1,7 @@ from bal.variant.pow_blockchain import POWBlockchain from time import time + class POWBlockchainSimulation(POWBlockchain): def __init__(self, p2p_port, initial_difficulty, simulation_path, name): super(POWBlockchainSimulation, self).__init__(p2p_port, initial_difficulty) @@ -10,15 +11,15 @@ def __init__(self, p2p_port, initial_difficulty, simulation_path, name): def after_send_transaction(self, tx): ts = time() tx_id = tx['id'] - with open(self.path + 'transaction_pool-'+tx_id+'-'+self.name+'.txt', 'a+') as file: - file.write(self.name + '---'+ 'sending tx' + '---' + str(ts)) + with open(self.path + 'transaction_pool-' + tx_id + '-' + self.name + '.txt', 'a+') as file: + file.write(self.name + '---' + 'sending tx' + '---' + str(ts)) file.write('\n') def after_handle_received_transaction(self, tx): ts = time() tx_id = tx['id'] - with open(self.path + 'transaction_pool-'+tx_id+'-'+self.name+'.txt', 'a+') as file: - file.write(self.name + '---'+ 'received tx' + '---' + str(ts)) + with open(self.path + 'transaction_pool-' + tx_id + '-' + self.name + '.txt', 'a+') as file: + file.write(self.name + '---' + 'received tx' + '---' + str(ts)) file.write('\n') def after_generate_raw_next_block(self, block): @@ -27,15 +28,15 @@ def after_generate_raw_next_block(self, block): if len(transactions) > 1: for tx in transactions[1:]: tx_id = tx['id'] - with open(self.path + 'transaction_block-'+tx_id+'-'+self.name+'.txt', 'a+') as file: - file.write(self.name + '---'+ 'sending tx with block ' + '---' + str(ts)) + with open(self.path + 'transaction_block-' + tx_id + '-' + self.name + '.txt', 'a+') as file: + file.write(self.name + '---' + 'sending tx with block ' + '---' + str(ts)) file.write('\n') def after_update_chain(self, block): ts = time() transactions = block['transactions'] - for tx in transactions[1:]: #ignore coinbase transaction + for tx in transactions[1:]: # ignore coinbase transaction tx_id = tx['id'] - with open(self.path + 'transaction_block-'+tx_id+'-'+self.name+'.txt', 'a+') as file: - file.write(self.name + '---'+ 'received tx with block' + '---' + str(ts)) + with open(self.path + 'transaction_block-' + tx_id + '-' + self.name + '.txt', 'a+') as file: + file.write(self.name + '---' + 'received tx with block' + '---' + str(ts)) file.write('\n') diff --git a/bal/wallet.py b/bal/wallet.py index b58834f..f5fa291 100644 --- a/bal/wallet.py +++ b/bal/wallet.py @@ -5,19 +5,23 @@ import json from functional import seq + def get_private_from_wallet(): sk = SigningKey.from_pem(open(PRIVATE_KEY_LOCATION).read()) return sk.to_der().encode('hex') + def get_public_from_wallet(): private_key = get_private_from_wallet() decoded = SigningKey.from_der(private_key.decode("hex")) return decoded.get_verifying_key().to_der().encode('hex') + def generate_private_key(): private_key = SigningKey.generate(curve=SECP256k1) return private_key + def init_wallet(keystore_path): global PRIVATE_KEY_LOCATION @@ -29,24 +33,28 @@ def init_wallet(keystore_path): try: with open(PRIVATE_KEY_LOCATION, "w") as f: f.write(new_private_key) - except: + except BaseException: return print('new wallet with private key created to : %s', PRIVATE_KEY_LOCATION) + def delete_wallet(): if os.path.exists(PRIVATE_KEY_LOCATION): - os.remove(PRIVATE_KEY_LOCATION) + os.remove(PRIVATE_KEY_LOCATION) else: - print("The file does not exist") + print("The file does not exist") def get_balance(address, unspent_tx_outs): - return seq(find_unspent_tx_outs(address, unspent_tx_outs))\ - .map(lambda u_tx_o: u_tx_o['amount'])\ - .sum() + return (seq(find_unspent_tx_outs(address, unspent_tx_outs)) + .map(lambda u_tx_o: u_tx_o['amount']) + .sum()) + def find_unspent_tx_outs(owner_address, unspent_tx_outs): - return [u_tx_o for u_tx_o in unspent_tx_outs or [] if u_tx_o['address'] == owner_address] + return [u_tx_o for u_tx_o in unspent_tx_outs or [] + if u_tx_o['address'] == owner_address] + def find_tx_outs_for_amount(amount, my_unspent_tx_outs): current_amount = 0 @@ -58,9 +66,10 @@ def find_tx_outs_for_amount(amount, my_unspent_tx_outs): left_over_amount = current_amount - amount return [included_unspent_tx_outs, left_over_amount] - e_msg = 'Cannot create transaction from the available unspent transaction outputs.' + \ - ' Required amount:' + str(amount) + '. Available unspentTxOuts:' + json.dumps(my_unspent_tx_outs) - raise Exception(e_msg) + exception_message = ('Cannot create transaction from the available unspent transaction outputs.' + + ' Required amount:' + str(amount) + '. Available unspentTxOuts:' + json.dumps(my_unspent_tx_outs)) + raise Exception(exception_message) + def create_tx_outs(receiver_address, my_address, amount, left_over_amount): tx_out1 = new_tx_out(receiver_address, amount) @@ -70,18 +79,24 @@ def create_tx_outs(receiver_address, my_address, amount, left_over_amount): left_over_tx = new_tx_out(my_address, left_over_amount) return [tx_out1, left_over_tx] + def filter_tx_pool_txs(unspent_tx_outs, transaction_pool): - tx_ins = seq(transaction_pool)\ - .map(lambda tx : tx['tx_ins'])\ - .flatten() + tx_ins = (seq(transaction_pool) + .map(lambda tx: tx['tx_ins']) + .flatten()) removable = [] for unspent_tx_out in unspent_tx_outs or []: - tx_in = find_unspent_tx_out(unspent_tx_out['tx_out_id'], unspent_tx_out['tx_out_index'], tx_ins) + tx_in = find_unspent_tx_out(unspent_tx_out['tx_out_id'], + unspent_tx_out['tx_out_index'], + tx_ins) if tx_in: removable.append(unspent_tx_out) return [tx for tx in unspent_tx_outs or [] if tx not in removable] -def create_transaction(receiver_address, amount, private_key, unspent_tx_outs, tx_pool): + +def create_transaction(receiver_address, amount, + private_key, unspent_tx_outs, + tx_pool): print('txPool: ', json.dumps(tx_pool)) decoded = SigningKey.from_der(private_key.decode("hex")) my_address = decoded.get_verifying_key().to_der().encode('hex') @@ -91,11 +106,12 @@ def create_transaction(receiver_address, amount, private_key, unspent_tx_outs, t included_unspent_tx_outs, left_over_amount = find_tx_outs_for_amount(amount, my_unspent_tx_outs) - - unsigned_tx_ins = [new_tx_in(utx_o['tx_out_id'], utx_o['tx_out_index'], None) for utx_o in included_unspent_tx_outs] + unsigned_tx_ins = [new_tx_in(utx_o['tx_out_id'], utx_o['tx_out_index'], None) + for utx_o in included_unspent_tx_outs] tx_ins = unsigned_tx_ins - tx_outs = create_tx_outs(receiver_address, my_address, amount, left_over_amount) + tx_outs = create_tx_outs(receiver_address, my_address, + amount, left_over_amount) tx = new_transaction(None, tx_ins, tx_outs) tx_id = get_transaction_id(tx) diff --git a/setup.py b/setup.py index 3d9dd32..99ce9a9 100644 --- a/setup.py +++ b/setup.py @@ -77,33 +77,33 @@ def run(self): sys.exit() -setup( - name=NAME, - version=about['__version__'], - description=DESCRIPTION, - long_description=long_description, - author=AUTHOR, - author_email=EMAIL, - python_requires=REQUIRES_PYTHON, - url=URL, - packages=find_packages(exclude=('tests',)), - install_requires=REQUIRED, - include_package_data=True, - license='GPL', - classifiers=[ - # Trove classifiers - # Full list: https://pypi.python.org/pypi?%3Aaction=list_classifiers - 'License :: OSI Approved :: GNU General Public License v3', - 'Programming Language :: Python', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: Implementation :: CPython', - 'Programming Language :: Python :: Implementation :: PyPy' - ], - # $ setup.py publish support. - cmdclass={ - 'upload': UploadCommand, - }, + +setup(name=NAME, + version=about['__version__'], + description=DESCRIPTION, + long_description=long_description, + author=AUTHOR, + author_email=EMAIL, + python_requires=REQUIRES_PYTHON, + url=URL, + packages=find_packages(exclude=('tests',)), + install_requires=REQUIRED, + include_package_data=True, + license='GPL', + classifiers=[ + # Trove classifiers + # Full list: https://pypi.python.org/pypi?%3Aaction=list_classifiers + 'License :: OSI Approved :: GNU General Public License v3', + 'Programming Language :: Python', + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: Implementation :: CPython', + 'Programming Language :: Python :: Implementation :: PyPy' + ], + # $ setup.py publish support. + cmdclass={ + 'upload': UploadCommand, + }, ) diff --git a/simulation/bcmn_random_simulation.py b/simulation/bcmn_random_simulation.py index 2ce8c8b..0db67cf 100644 --- a/simulation/bcmn_random_simulation.py +++ b/simulation/bcmn_random_simulation.py @@ -25,34 +25,53 @@ from bcmn_simulation import * from simulation_tools import * -def simulate(host_type, host_number, max_bw, miner_percentages, transaction_counts, simulation_count, root_path, debug_mode): + +def simulate(host_type, host_number, + max_bw, miner_percentages, + transaction_counts, simulation_count, + root_path, debug_mode): switch_number = host_number / 4 or 1 edge_number = switch_number topo = rtg.mininet_topo(switch_number, edge_number, host_number, max_bw) - net_params = {'topo': topo, 'build': False, 'host': host_type, 'switch': OVSBridge, - 'link': TCLink, 'ipBase': '10.0.0.0/8', 'waitConnected' : True, - 'xterms': debug_mode} - - for miner_percentage, transaction_count in itertools.product(miner_percentages, transaction_counts): + net_params = {'topo': topo, + 'build': False, + 'host': host_type, + 'switch': OVSBridge, + 'link': TCLink, + 'ipBase': '10.0.0.0/8', + 'waitConnected': True, + 'xterms': debug_mode} + + for miner_percentage, transaction_count \ + in itertools.product(miner_percentages, transaction_counts): + for i in range(0, simulation_count): print("Simulating with miner %: " + str(miner_percentage) - + ", transaction #: " + str(transaction_count)) - parametered_path = 'h' + str(host_number) + 'm' + str(miner_percentage) + 't' + str(transaction_count) + + ", transaction #: " + str(transaction_count)) + parametered_path = ('h' + str(host_number) + + 'm' + str(miner_percentage) + + 't' + str(transaction_count)) path = root_path + parametered_path - miner_number = (len(topo.hosts())*miner_percentage / 100) or 1 + miner_number = (len(topo.hosts()) * miner_percentage / 100) or 1 miner_names = random.sample(topo.hosts(), miner_number) - transactor_names = [random.choice(topo.hosts()) for x in range(transaction_count)] - subsimulation(net_params, miner_names, transactor_names, path, debug_mode) - -def subsimulation(net_params, miner_names, transactor_names, sim_path, debug_mode): + transactor_names = [random.choice(topo.hosts()) for x + in range(transaction_count)] + subsimulation(net_params, miner_names, + transactor_names, path, + debug_mode) + +def subsimulation(net_params, miner_names, + transactor_names, sim_path, + debug_mode): net = None try: start_time = time() timestamp_str = str(int(start_time)) - ts_dir_path = init_simulation_path(sim_path + '/' + timestamp_str + '/') + ts_dir_path = init_simulation_path(sim_path + '/' + + timestamp_str + '/') net = Mininet(**net_params) net.build() @@ -61,11 +80,12 @@ def subsimulation(net_params, miner_names, transactor_names, sim_path, debug_mod for node in net.hosts: node.start(ts_dir_path) - sleep(2) # Wait for nodes to be started completely. + sleep(2) # Wait for nodes to be started completely. peer_topology = register_peer_topology(net) miners = [host for host in net.hosts if host.name in miner_names] - transactors = [net.getNodeByName(host_name) for host_name in transactor_names] + transactors = [net.getNodeByName(host_name) + for host_name in transactor_names] edge_list = rtg.topo_to_edgelist(net.topo) dump_graph(edge_list, ts_dir_path) dump_net(net, peer_topology, miners, ts_dir_path) @@ -86,9 +106,10 @@ def subsimulation(net_params, miner_names, transactor_names, sim_path, debug_mod if h.name in generated: continue host_amount = verifier_check_amount(h, verifier) - print(h.name + ' has ' + str(host_amount) + ' coins currently, target is: ' + str(target_amount)) + print(h.name + ' has ' + str(host_amount) + ' coins currently, ' + + 'target is: ' + str(target_amount)) if (host_amount >= target_amount): - print(h.name + ' has enough coins, stopping generation for it') + print(h.name +' has enough coins, stopping generation for it') h.call('block/generate/loop/stop', True) generated.append(h.name) @@ -101,7 +122,7 @@ def subsimulation(net_params, miner_names, transactor_names, sim_path, debug_mod sender = random.choice(transactors) receiver = random.choice([n for n in net.hosts if n != sender]) current_block_number = yaml.safe_load(sender.call('chain/length', True)) - while current_block_number <= temp_block_number: # wait for next block to be forged before new tx + while current_block_number <= temp_block_number: # wait for next block to be forged before new tx sleep(BLOCK_GENERATION_INTERVAL) current_block_number = yaml.safe_load(sender.call('chain/length', True)) @@ -112,7 +133,9 @@ def subsimulation(net_params, miner_names, transactor_names, sim_path, debug_mod print('Waiting for nodes to receive transactions') host_number = len(net.hosts) - while not check_block_txts(ts_dir_path, host_number, transaction_count): + while not check_block_txts(ts_dir_path, + host_number, + transaction_count): sleep(0.5) elapsed_time = time() - start_time @@ -120,7 +143,7 @@ def subsimulation(net_params, miner_names, transactor_names, sim_path, debug_mod dump_chain(verifier, ts_dir_path) net.stop() move_txs_to_directories(ts_dir_path) - except: + except BaseException: if net: net.stop() traceback.print_exc() @@ -130,22 +153,39 @@ def subsimulation(net_params, miner_names, transactor_names, sim_path, debug_mod def main(): host_type = None parser = ArgumentParser() - parser.add_argument('-ht', '--host_type', default='pow', type=str, help='blockchain consensus class to be used') - parser.add_argument('-p', '--path', default='/tmp/', type=str, help='where the logs will be located. default: /tmp/') - parser.add_argument('-d', '--debug', default=False, help='debug mode for xterms.', action='store_true') - parser.add_argument('-s', '--setup', default=False, help='go with setup mode for h=10,20,50,100 m=10,20,50,80 tx=10, sim=10', action='store_true') + + help_message = 'blockchain consensus class to be used' + parser.add_argument('-ht', '--host_type', + default='pow', type=str, + help=help_message) + + help_message = 'where the logs will be located. default: /tmp/' + parser.add_argument('-p', '--path', + default='/tmp/', type=str, + help=help_message) + + help_message = 'debug mode for xterms.' + parser.add_argument('-d', '--debug', + default=False, help=help_message, + action='store_true') + + help_message = 'go with setup mode for h=10,20,50,100 m=10,20,50,80 tx=10, sim=10' + parser.add_argument('-s', '--setup', + default=False, help=help_message, + action='store_true') + args = parser.parse_args() tmp_location = '/tmp/bcn' if os.path.exists(tmp_location): shutil.rmtree('/tmp/bcn') - setLogLevel( 'info' ) + setLogLevel('info') if args.host_type.find('pos') == 0: host_type = POSNode else: host_type = POWNode - #make this separate arguments + # make this separate arguments if args.setup: host_numbers = [10, 20, 50, 100] miner_percentages = [0, 10, 25, 50, 100] @@ -153,7 +193,10 @@ def main(): simulation_count = 5 max_bw = 100 for host_number in host_numbers: - simulate(host_type, host_number, max_bw, miner_percentages, transaction_counts, simulation_count, args.path, args.debug) + simulate(host_type, host_number, + max_bw, miner_percentages, + transaction_counts, simulation_count, + args.path, args.debug) else: host_number = int(input("Number of hosts(>10):")) @@ -161,6 +204,11 @@ def main(): miner_percentage = int(input("Miner percentage (0-100):")) transaction_count = int(input("Number of repeated random transactions:")) simulation_count = int(input("Number of repeated simulations:")) - simulate(host_type, host_number, max_bw, [miner_percentage], [transaction_count], simulation_count, args.path, args.debug) + simulate(host_type, host_number, + max_bw, [miner_percentage], + [transaction_count], simulation_count, + args.path, args.debug) + + if __name__ == '__main__': main() diff --git a/simulation/bcmn_simulation.py b/simulation/bcmn_simulation.py index cc3b967..e4be241 100644 --- a/simulation/bcmn_simulation.py +++ b/simulation/bcmn_simulation.py @@ -12,10 +12,13 @@ import shutil import getopt + def simulate(host_type): net = None try: - net = Mininet( topo=None, build=False, host=host_type, ipBase='10.0.0.0/8', xterms=True, waitConnected=True) + net = Mininet(topo=None, build=False, + host=host_type, ipBase='10.0.0.0/8', + xterms=True, waitConnected=True) h1 = net.addHost('h1', ip='10.0.0.1', defaultRoute=None) h2 = net.addHost('h2', ip='10.0.0.2', defaultRoute=None) @@ -35,7 +38,7 @@ def simulate(host_type): for node in net.hosts: node.start('/tmp/') - sleep(2) # Wait for nodes to be started completely. + sleep(2) # Wait for nodes to be started completely. generators = [h1, h2, h3, h4] for node in generators: @@ -71,7 +74,7 @@ def simulate(host_type): send_transaction(h3, h5, 35) wait_and_forge_transactions(verifier, 2) - #8/11/10 Phase1 + # 8/11/10 Phase1 open_mininet_cli(net) h6 = add_host_helper('h6', '10.0.0.6', s2, net) h7 = add_host_helper('h7', '10.0.0.7', s2, net) @@ -89,13 +92,13 @@ def simulate(host_type): send_transaction(h4, h8, 15) wait_and_forge_transactions(verifier, 3) - #8/11/10 between entity + # 8/11/10 between entity raw_input('Input something to send mid-transactions(between entity) in 8/11/10') send_transaction(h6, h5, 5) send_transaction(h7, h5, 30) wait_and_forge_transactions(verifier, 2) - #8/11/10 Phase2 + # 8/11/10 Phase2 open_mininet_cli(net) h9 = add_host_helper('h9', '10.0.0.9', s1, net) h10 = add_host_helper('h10', '10.0.0.10', s1, net) @@ -107,12 +110,12 @@ def simulate(host_type): send_transaction(h5, h10, 90) wait_and_forge_transactions(verifier, 2) - #8/11/10 Phase3 + # 8/11/10 Phase3 raw_input('Input something to send transactions in 8/11/10 Phase3: ') send_transaction(h10, verifier, 90) wait_and_forge_transactions(verifier, 1) - #15/06/2011 + # 15/06/2011 open_mininet_cli(net) h11 = add_host_helper('h11', '10.0.0.11', s1, net) register_peers(h8, h11) @@ -121,7 +124,7 @@ def simulate(host_type): send_transaction(h8, h11, 15) wait_and_forge_transactions(verifier, 1) - #17/06/2011: Phase1 + # 17/06/2011: Phase1 open_mininet_cli(net) h12 = add_host_helper('h12', '10.0.0.12', s1, net) register_peers(h11, h12) @@ -129,25 +132,27 @@ def simulate(host_type): raw_input('Input something to send transactions in 17/06/2011: Phase1: ') target_amount = 12 transaction_count = 8 - print("Sending " + str(transaction_count) + " number of transactions from h11 to h12") + print("Sending " + str(transaction_count) + + " number of transactions from h11 to h12") for i in range(1, transaction_count + 1): send_transaction(h11, h12, target_amount/float(transaction_count), True) wait_and_forge_transactions(verifier, 1) sleep(0.5) - #17/06/2011: Phase2 + # 17/06/2011: Phase2 raw_input('Input something to send transactions in 17/06/2011 Phase2: ') send_transaction(h12, verifier, 12) wait_and_forge_transactions(verifier, 1) finally: - result=CLI(net) + result = CLI(net) net.stop() + def main(): host_type = None try: - opts, args = getopt.getopt(sys.argv[1:],"ht:",["host_type="]) + opts, args = getopt.getopt(sys.argv[1:], "ht:", ["host_type="]) except getopt.GetoptError: print 'bcmn_simulation -ht ' sys.exit(2) @@ -170,8 +175,9 @@ def main(): tmp_location = '/tmp/bcn' if os.path.exists(tmp_location): shutil.rmtree('/tmp/bcn') - setLogLevel( 'info' ) + setLogLevel('info') simulate(host_type) + if __name__ == '__main__': main() diff --git a/simulation/complex_networks/bcmn_k_shell_simulation.py b/simulation/complex_networks/bcmn_k_shell_simulation.py index 2f0e541..68732d7 100644 --- a/simulation/complex_networks/bcmn_k_shell_simulation.py +++ b/simulation/complex_networks/bcmn_k_shell_simulation.py @@ -31,6 +31,7 @@ flatten = itertools.chain.from_iterable + def simulate(host_number, number_of_transactions, root_path): timestamp_str = str(int(time())) edge_number = 2 * host_number @@ -38,7 +39,7 @@ def simulate(host_number, number_of_transactions, root_path): while len(unique_cores) < 3: adj_matrix = rtg.random_connected_graph(host_number, edge_number) edge_list = rtg.graph_to_str(adj_matrix) - G = nx.parse_edgelist(edge_list, nodetype = int) + G = nx.parse_edgelist(edge_list, nodetype=int) core_numbers = nx.core_number(G) unique_cores = list(set(core_numbers.values())) @@ -46,15 +47,29 @@ def simulate(host_number, number_of_transactions, root_path): for core_number in unique_cores: print("Simulating with miner in ks:" + str(core_number)) miner_number = 1 - miner_names = ['h'+ str(name) for name, core in core_numbers.items() if core == core_number][:miner_number] - subsimulation(adj_matrix, host_number, core_number, miner_names, root_path, number_of_transactions, timestamp_str, edge_list) - -def subsimulation(adj_matrix, host_number, k_shell_miner, miner_names, root_path, number_of_transactions, timestamp_str, edge_list): + miner_names = ['h' + str(name) for name, core in core_numbers.items() + if core == core_number][:miner_number] + subsimulation(adj_matrix, host_number, + core_number, miner_names, + root_path, number_of_transactions, + timestamp_str, edge_list) + + +def subsimulation(adj_matrix, host_number, + k_shell_miner, miner_names, + root_path, number_of_transactions, + timestamp_str, edge_list): net = None try: start_time = time() - net_params = {'topo': None, 'build': False, 'host': POWNode, 'switch': OVSBridge, - 'link': TCLink, 'ipBase': '10.0.0.0/8', 'waitConnected' : True, 'xterms': False} + net_params = {'topo': None, + 'build': False, + 'host': POWNode, + 'switch': OVSBridge, + 'link': TCLink, + 'ipBase': '10.0.0.0/8', + 'waitConnected': True, + 'xterms': False} net = rtg.mininet_topo(adj_matrix, net_params) net.build() net.start() @@ -67,7 +82,7 @@ def subsimulation(adj_matrix, host_number, k_shell_miner, miner_names, root_path for node in net.hosts: node.start(ts_dir_path) - sleep(2) # Wait for nodes to be started completely. + sleep(2) # Wait for nodes to be started completely. peer_topology = register_peer_topology(net) @@ -75,8 +90,11 @@ def subsimulation(adj_matrix, host_number, k_shell_miner, miner_names, root_path target_amount = 10 target_number = 3 - random_generator_number = target_number - len(miners) if target_number > len(miners) else 0 - random_generator_hosts = random.sample([x for x in net.hosts if x not in miners], random_generator_number) + random_generator_number = (target_number - len(miners) + if target_number > len(miners) + else 0) + random_generator_hosts = random.sample([x for x in net.hosts if x not in miners], + random_generator_number) generators = miners + random_generator_hosts for node in generators: @@ -91,7 +109,8 @@ def subsimulation(adj_matrix, host_number, k_shell_miner, miner_names, root_path if h.name in generated: continue host_amount = verifier_check_amount(h, verifier) - print(h.name + ' has ' + str(host_amount) + ' coins currently, target is: ' + str(target_amount)) + print(h.name + ' has ' + str(host_amount) + + ' coins currently, target is: ' + str(target_amount)) if (host_amount >= target_amount): print(h.name + ' has enough coins, stopping generation for it') h.call('block/generate/loop/stop', True) @@ -124,12 +143,13 @@ def subsimulation(adj_matrix, host_number, k_shell_miner, miner_names, root_path dump_chain(verifier, ts_dir_path) net.stop() move_txs_to_directories(ts_dir_path) - except: + except BaseException: if net: open_mininet_cli(net) net.stop() traceback.print_exc() + def register_peer_topology(net): print("Registering peers") peers_by_switch = [] @@ -148,18 +168,22 @@ def register_peer_topology(net): return peers_by_switch + def main(): host_type = None parser = ArgumentParser() - parser.add_argument('-p', '--path', default='/tmp/', type=str, help='where the logs will be located. default: /tmp/') + help_message = 'where the logs will be located. default: /tmp/' + parser.add_argument('-p', '--path', default='/tmp/', + type=str, help=help_message) args = parser.parse_args() tmp_location = '/tmp/bcn' if os.path.exists(tmp_location): shutil.rmtree('/tmp/bcn') - setLogLevel( 'info' ) + setLogLevel('info') host_number = int(input("Number of hosts(>10):")) number_of_transactions = int(input("Number of repeated random transactions:")) simulate(host_number, number_of_transactions, args.path) + if __name__ == '__main__': main() diff --git a/simulation/complex_networks/bcmn_real_world_simulation.py b/simulation/complex_networks/bcmn_real_world_simulation.py index b352c88..2780407 100644 --- a/simulation/complex_networks/bcmn_real_world_simulation.py +++ b/simulation/complex_networks/bcmn_real_world_simulation.py @@ -31,25 +31,40 @@ flatten = itertools.chain.from_iterable + def simulate(host_number, number_of_transactions, root_path): timestamp_str = str(int(time())) k = 4 wiring_p = 0.0 repeat = 5 - for x in range(repeat+1): + for x in range(repeat + 1): print("Simulating with rewiring probability:" + str(wiring_p)) - G = nx.connected_watts_strogatz_graph(host_number, k ,wiring_p) + G = nx.connected_watts_strogatz_graph(host_number, k, wiring_p) adj_matrix = rtg.nx_graph_to_adj_matrix(G) edge_list = rtg.graph_to_str(adj_matrix) - subsimulation(adj_matrix, host_number, wiring_p, root_path, number_of_transactions, timestamp_str, edge_list) - wiring_p = wiring_p + 1.0/repeat + subsimulation(adj_matrix, host_number, + wiring_p, root_path, + number_of_transactions, timestamp_str, + edge_list) + wiring_p = wiring_p + 1.0 / repeat + -def subsimulation(adj_matrix, host_number, wiring_p, root_path, number_of_transactions, timestamp_str, edge_list): +def subsimulation(adj_matrix, host_number, + wiring_p, root_path, + number_of_transactions, timestamp_str, + edge_list): net = None try: start_time = time() - net_params = {'topo': None, 'build': False, 'host': POWNode, 'switch': OVSBridge, - 'link': TCLink, 'ipBase': '10.0.0.0/8', 'waitConnected' : True, 'xterms': False} + net_params = { + 'topo': None, + 'build': False, + 'host': POWNode, + 'switch': OVSBridge, + 'link': TCLink, + 'ipBase': '10.0.0.0/8', + 'waitConnected': True, + 'xterms': False} net = rtg.mininet_topo(adj_matrix, net_params) net.build() net.start() @@ -58,12 +73,14 @@ def subsimulation(adj_matrix, host_number, wiring_p, root_path, number_of_transa miners = random.sample(net.hosts, miner_number) verifier = random.choice(miners) parametered_path = 'h' + str(host_number) + 'p' + str(wiring_p) - ts_dir_path = init_simulation_path(root_path + parametered_path + '/' + timestamp_str + '/') + ts_dir_path = init_simulation_path(root_path + + parametered_path + '/' + + timestamp_str + '/') for node in net.hosts: node.start(ts_dir_path) - sleep(2) # Wait for nodes to be started completely. + sleep(2) # Wait for nodes to be started completely. peer_topology = register_peer_topology(net) @@ -73,7 +90,8 @@ def subsimulation(adj_matrix, host_number, wiring_p, root_path, number_of_transa target_amount = 10 target_number = 3 random_generator_number = target_number - len(miners) if target_number > len(miners) else 0 - random_generator_hosts = random.sample([x for x in net.hosts if x not in miners], random_generator_number) + random_generator_hosts = random.sample([x for x in net.hosts if x not in miners], + random_generator_number) generators = miners + random_generator_hosts for node in generators: @@ -88,7 +106,8 @@ def subsimulation(adj_matrix, host_number, wiring_p, root_path, number_of_transa if h.name in generated: continue host_amount = verifier_check_amount(h, verifier) - print(h.name + ' has ' + str(host_amount) + ' coins currently, target is: ' + str(target_amount)) + print(h.name + ' has ' + str(host_amount) + ' coins currently, ' + + 'target is: ' + str(target_amount)) if (host_amount >= target_amount): print(h.name + ' has enough coins, stopping generation for it') h.call('block/generate/loop/stop', True) @@ -113,7 +132,9 @@ def subsimulation(adj_matrix, host_number, wiring_p, root_path, number_of_transa temp_block_number = yaml.safe_load(sender.call('chain/length', True)) print('Waiting for nodes to receive transactions') - while not check_block_txts(ts_dir_path, host_number, number_of_transactions): + while not check_block_txts(ts_dir_path, + host_number, + number_of_transactions): sleep(0.5) elapsed_time = time() - start_time @@ -121,17 +142,19 @@ def subsimulation(adj_matrix, host_number, wiring_p, root_path, number_of_transa dump_chain(verifier, ts_dir_path) net.stop() move_txs_to_directories(ts_dir_path) - except: + except BaseException: if net: open_mininet_cli(net) net.stop() traceback.print_exc() + def dump_graph(edge_list, dir_path): with open(dir_path + 'graph.txt', 'w') as file: # Use file to refer to the file object file.write(str(edge_list)) file.write('\n') + def register_peer_topology(net): print("Registering peers") peers_by_switch = [] @@ -150,18 +173,22 @@ def register_peer_topology(net): return peers_by_switch + def main(): host_type = None parser = ArgumentParser() - parser.add_argument('-p', '--path', default='/tmp/', type=str, help='where the logs will be located. default: /tmp/') + help_message = 'where the logs will be located. default: /tmp/' + parser.add_argument('-p', '--path', default='/tmp/', + type=str, help=help_message) args = parser.parse_args() tmp_location = '/tmp/bcn' if os.path.exists(tmp_location): shutil.rmtree('/tmp/bcn') - setLogLevel( 'info' ) + setLogLevel('info') host_number = int(input("Number of hosts(>10):")) number_of_transactions = int(input("Number of repeated random transactions:")) simulate(host_number, number_of_transactions, args.path) + if __name__ == '__main__': main() diff --git a/simulation/complex_networks/complex_random_topology_generator.py b/simulation/complex_networks/complex_random_topology_generator.py index 168e9e0..3c9f18a 100644 --- a/simulation/complex_networks/complex_random_topology_generator.py +++ b/simulation/complex_networks/complex_random_topology_generator.py @@ -10,20 +10,25 @@ import sys import networkx as nx import numpy as np -#make a class this module +# make a class this module + + def graph_to_str(adj_matrix): str_matrix = [] v = int(math.sqrt(len(adj_matrix))) - for i in range (1, v): - for j in range(i+1, v+1): - index = ( i - 1 ) * v + j - 1 - if adj_matrix[ index ]: + for i in range(1, v): + for j in range(i + 1, v + 1): + index = (i - 1) * v + j - 1 + if adj_matrix[index]: str_matrix.append(str(i) + " " + str(j)) return str_matrix # Return a random integer between 0 and k-1 inclusive. -def ran( k ): - return random.randint(0, k-1) + + +def ran(k): + return random.randint(0, k - 1) + def nx_graph_to_adj_matrix(G): nmp_matrix = nx.to_numpy_matrix(G, dtype=int) @@ -31,70 +36,76 @@ def nx_graph_to_adj_matrix(G): adj_matrix = np.squeeze(np.asarray(fl)) return adj_matrix + def random_connected_graph(v, e): adj_matrix = [0] * v * v tree = [0] * v init_array(tree, v) tree = permute(tree) for i in range(1, v): - j = ran( i ) - adj_matrix[ tree[ i ] * v + tree[ j ] ] = 1 - adj_matrix[ tree[ j ] * v + tree[ i ] ] = 1 + j = ran(i) + adj_matrix[tree[i] * v + tree[j]] = 1 + adj_matrix[tree[j] * v + tree[i]] = 1 count = v - 1 while count < e: - i = ran( v ) - j = ran( v ) + i = ran(v) + j = ran(v) if i == j: continue - if i > j : + if i > j: i, j = j, i index = i * v + j - if not adj_matrix[ index ]: - adj_matrix[ index ] = 1 + if not adj_matrix[index]: + adj_matrix[index] = 1 count += 1 return adj_matrix + def permute(arr): return random_permutation(arr) + def random_permutation(iterable, r=None): "Random selection from itertools.permutations(iterable, r)" pool = tuple(iterable) r = len(pool) if r is None else r return tuple(random.sample(pool, r)) + def init_array(arr, end): - for i in range(0, end): - arr[i] = i + for i in range(0, end): + arr[i] = i + def mininet_topo(adj_matrix, net_params): host_number = int(math.sqrt(len(adj_matrix))) net = Mininet(**net_params) switches = [None] * host_number - for i in range(1, host_number+1): - switches[i-1] = net.addSwitch('s' + str(i), failMode = 'standalone', stp=1) + for i in range(1, host_number + 1): + switches[i - 1] = net.addSwitch('s' + str(i), failMode='standalone', stp=1) - for i in range(1, host_number+1): - host = net.addHost('h'+ str(i), defaultRoute=None) - selected_sw = switches[i-1] + for i in range(1, host_number + 1): + host = net.addHost('h' + str(i), defaultRoute=None) + selected_sw = switches[i - 1] net.addLink(selected_sw, host) - for i in range (1, host_number): - for j in range(i+1, host_number+1): - index = ( i - 1 ) * host_number + j - 1 - if adj_matrix[ index ]: - net.addLink(switches[i-1], switches[j-1]) + for i in range(1, host_number): + for j in range(i + 1, host_number + 1): + index = (i - 1) * host_number + j - 1 + if adj_matrix[index]: + net.addLink(switches[i - 1], switches[j - 1]) return net + if __name__ == '__main__': - setLogLevel( 'info' ) + setLogLevel('info') host_number = int(input("Number of hosts:")) net_params = {'switch': OVSBridge, 'link': TCLink, 'host': CPULimitedHost, - 'ipBase': '10.0.0.0/8', 'waitConnected' : True} + 'ipBase': '10.0.0.0/8', 'waitConnected': True} edge_number = 2 * host_number adj_matrix = random_connected_graph(host_number, edge_number) net = mininet_topo(adj_matrix, net_params) net.build() net.start() - CLI( net ) + CLI(net) net.stop() diff --git a/simulation/random_topology_generator.py b/simulation/random_topology_generator.py index d5d8234..f47a827 100644 --- a/simulation/random_topology_generator.py +++ b/simulation/random_topology_generator.py @@ -9,14 +9,19 @@ import networkx as nx import math + def topo_to_edgelist(topo): G = topo.convertTo(nx.MultiGraph) - edge_list_str = [str(u) + ' ' + str(v) + ' ' + str(bw) for u,v,bw in G.edges(data='bw')] + edge_list_str = [str(u) + ' ' + str(v) + ' ' + str(bw) + for u, v, bw in G.edges(data='bw')] return edge_list_str # Return a random integer between 0 and k-1 inclusive. -def ran( k ): - return random.randint(0, k-1) + + +def ran(k): + return random.randint(0, k - 1) + def random_connected_graph(v, e): adj_matrix = [0] * v * v @@ -26,79 +31,85 @@ def random_connected_graph(v, e): tree = permute(tree) for i in range(1, v): - j = ran( i ) - adj_matrix[ tree[ i ] * v + tree[ j ] ] = 1 - adj_matrix[ tree[ j ] * v + tree[ i ] ] = 1 + j = ran(i) + adj_matrix[tree[i] * v + tree[j]] = 1 + adj_matrix[tree[j] * v + tree[i]] = 1 count = v - 1 - max_edge = v * (v - 1)/2 + max_edge = v * (v - 1) / 2 e = max_edge if e > max_edge else e while count < e: - i = ran( v ) - j = ran( v ) + i = ran(v) + j = ran(v) if i == j: continue - if i > j : + if i > j: i, j = j, i index = i * v + j - if not adj_matrix[ index ]: - adj_matrix[ index ] = 1 + if not adj_matrix[index]: + adj_matrix[index] = 1 count += 1 return adj_matrix + def permute(arr): return random_permutation(arr) + def random_permutation(iterable, r=None): "Random selection from itertools.permutations(iterable, r)" pool = tuple(iterable) r = len(pool) if r is None else r return tuple(random.sample(pool, r)) + def init_array(arr, end): - for i in range(0, end): - arr[i] = i + for i in range(0, end): + arr[i] = i + def mininet_topo(switch_number, edge_number, host_number, max_bw): switch_matrix = random_connected_graph(switch_number, edge_number) net = Topo() switches = [None] * switch_number - for i in range(1, switch_number+1): - switches[i-1] = net.addSwitch('s' + str(i), failMode = 'standalone', stp=1) + for i in range(1, switch_number + 1): + switches[i - 1] = net.addSwitch('s' + str(i), failMode='standalone', stp=1) - for i in range(1, host_number+1): - ran_bw = ran(max_bw)+1 - cpu_f = (ran_bw*1.0 / max_bw) - host = net.addHost('h'+ str(i), defaultRoute=None, cpu=cpu_f) + for i in range(1, host_number + 1): + ran_bw = ran(max_bw) + 1 + cpu_f = (ran_bw * 1.0 / max_bw) + host = net.addHost('h' + str(i), defaultRoute=None, cpu=cpu_f) selected_sw = random.choice(switches) net.addLink(selected_sw, host, bw=ran_bw) - for i in range (1, switch_number): - for j in range(i+1, switch_number+1): - index = ( i - 1 ) * switch_number + j - 1 - if switch_matrix[ index ]: - net.addLink(switches[i-1], switches[j-1], bw=max_bw) + for i in range(1, switch_number): + for j in range(i + 1, switch_number + 1): + index = (i - 1) * switch_number + j - 1 + if switch_matrix[index]: + net.addLink(switches[i - 1], switches[j - 1], bw=max_bw) return net + if __name__ == '__main__': - setLogLevel( 'info' ) + setLogLevel('info') switch_number = int(input("Number of switches:")) host_number = int(input("Number of hosts:")) edge_number = int(input("Number of minimum links:")) max_bw = int(input("Maximum Bandwidth:")) - print("switch=%d hosts=%d minLinks=%d MaxBW=%d\n" % (switch_number, host_number, edge_number, max_bw)) + print("switch=%d hosts=%d minLinks=%d MaxBW=%d\n" % + (switch_number, host_number, edge_number, max_bw)) topo = mininet_topo(switch_number, edge_number, host_number, max_bw) net_params = {'switch': OVSBridge, 'link': TCLink, 'host': CPULimitedHost, - 'ipBase': '10.0.0.0/8', 'waitConnected' : True, 'topo' : topo, - 'build': False} + 'ipBase': '10.0.0.0/8', 'waitConnected': True, 'topo': topo, + 'build': False} net = Mininet(**net_params) net.build() net.start() - CLI( net ) + CLI(net) net.stop() diff --git a/simulation/simulation_tools.py b/simulation/simulation_tools.py index 10a76b7..71c62ce 100644 --- a/simulation/simulation_tools.py +++ b/simulation/simulation_tools.py @@ -7,17 +7,20 @@ import itertools flatten = itertools.chain.from_iterable + def send_and_log_transaction(from_host, to_host, amount, dir_path): - is_tx_creatable = yaml.safe_load(from_host.call('transactions/has_amount/'+str(amount), silent=True)) + is_tx_creatable = yaml.safe_load(from_host.call('transactions/has_amount/' + str(amount), silent=True)) if is_tx_creatable: - send_transaction(from_host,to_host,amount) + send_transaction(from_host, to_host, amount) with open(dir_path + 'activity.txt', 'a+') as file: # Use file to refer to the file object - file.write(from_host.name + ' sends transaction to ' + to_host.name + ' amount: ' + str(amount)) + file.write(from_host.name + ' sends transaction to ' + to_host.name + + ' amount: ' + str(amount)) file.write('\n') return True else: return False + def get_switch_map(net): switch_map = defaultdict(lambda: defaultdict(dict)) max_bw_map = {} @@ -28,7 +31,7 @@ def get_switch_map(net): to_intf = link.intf2 if issubclass(type(to_intf.node), Host): host_name = to_intf.node.name - bandwith = to_intf.params.get('bw',100) + bandwith = to_intf.params.get('bw', 100) temp_val = max([vals for vals in switch_map[switch_name]['hosts'].values()] or [0]) if temp_val < bandwith: @@ -40,6 +43,7 @@ def get_switch_map(net): return switch_map, max_bw_map + def register_peer_topology(net): print("Registering peers") peers_by_switch = [] @@ -64,15 +68,18 @@ def register_peer_topology(net): return peers_by_switch + list(peers_by_max_bw) + def dump_chain(host, dir_path): with open(dir_path + 'chain.txt', 'w') as file: # Use file to refer to the file object file.write(host.call('chain', True)) + def dump_elapsed_time(elapsed_time, dir_path): with open(dir_path + 'activity.txt', 'a+') as file: # Use file to refer to the file object file.write('Elapsed time for simulation(in sec):' + str(elapsed_time)) file.write('\n') + def dump_net(net, peer_topology, miners, dir_path): with open(dir_path + 'dump.txt', 'w') as file: # Use file to refer to the file object for node in net.switches + net.hosts: @@ -99,47 +106,56 @@ def dump_net(net, peer_topology, miners, dir_path): file.write(miner.name) file.write('\n') + def dump_graph(edge_list, dir_path): with open(dir_path + 'graph.txt', 'w') as file: # Use file to refer to the file object file.write(str(edge_list)) file.write('\n') + def init_simulation_path(path): if not os.path.exists(path): os.makedirs(path) return path + def check_block_txts(dir_path, host_number, tx_number): - block_txts = [filename for filename in os.listdir(dir_path) if filename.startswith("transaction_block")] + block_txts = [filename for filename in os.listdir(dir_path) + if filename.startswith("transaction_block")] if (not block_txts) or len(block_txts) < tx_number * host_number: return False return True + def move_txs_to_directories(dir_path): move_txs_to_directories_helper(dir_path, "transaction_block") move_txs_to_directories_helper(dir_path, "transaction_pool") + def move_txs_to_directories_helper(dir_path, file_prefix): - filenames = [filename for filename in os.listdir(dir_path) if filename.startswith(file_prefix)] + filenames = [filename for filename in os.listdir(dir_path) + if filename.startswith(file_prefix)] new_path = dir_path + file_prefix + '/' init_simulation_path(new_path) for fname in filenames: - tx_hash = fname.rsplit('-',2)[1] - with open(new_path + tx_hash + '.txt' , 'a+') as outfile: - with open(dir_path + fname) as infile: - outfile.write(infile.read()) - os.remove(dir_path + fname) + tx_hash = fname.rsplit('-', 2)[1] + with open(new_path + tx_hash + '.txt', 'a+') as outfile: + with open(dir_path + fname) as infile: + outfile.write(infile.read()) + os.remove(dir_path + fname) -def send_transaction(from_host, to_host, amount, silent = False): +def send_transaction(from_host, to_host, amount, silent=False): to_host_addr = yaml.safe_load(to_host.call('address/my', True))['address'] transaction_param = '{"recipient": "%s", "amount": %f}' % (to_host_addr, amount) from_host.call('transactions/send', silent, transaction_param) + def register_peers(from_host, to_host): peer_param = '{"peer": "%s:%s"}' % (to_host.IP(), to_host.socket) from_host.call('peers/register', True, peer_param) + def wait_and_forge_transactions(verifier, transaction_number): current_transaction_pool = yaml.safe_load(verifier.call('transactions/pool', True)) while len(current_transaction_pool) < transaction_number: @@ -147,6 +163,7 @@ def wait_and_forge_transactions(verifier, transaction_number): current_transaction_pool = yaml.safe_load(verifier.call('transactions/pool', True)) verifier.call('block/generate') + def add_host_helper(host_name, ip, switch, net): host = net.addHost(host_name, ip=ip, defaultRoute=None) link = net.addLink(host, switch) @@ -157,11 +174,13 @@ def add_host_helper(host_name, ip, switch, net): makeTerms([host]) return host + def verifier_check_amount(host, verifier): host_addr = yaml.safe_load(host.call('address/my', True))['address'] req = 'balance/%s' % (host_addr,) return yaml.safe_load(verifier.call(req, True))['balance'] + def open_mininet_cli(net): print('Opening Mininet CLI before changing current topology (Closing CLI will resume the script)') - result=CLI(net) + result = CLI(net)