def setUp(self): self.tp = TransactionPool() self.wallet = Wallet() self.recipient = 'r3co09ow99' self.transaction = Transaction.new_transaction(self.wallet, self.recipient, 30) self.tp.update_or_add(self.transaction)
def test_add_same_transaction(blockchain: Blockchain): transaction = to_transaction((63, [(63, 27)])) block = Block(blockchain.get_last_block(), [transaction]) block.mine(blockchain.get_difficult()) blockchain.add(block) transactionPool = TransactionPool() assert not transactionPool.receive_transaction(blockchain, transaction)
def test_receive_transaction(transactionPool: TransactionPool, blockchain: Blockchain): transaction = to_transaction((63, [(43, 28)])) assert transactionPool.receive_transaction(blockchain, transaction) transaction = to_transaction((63, [(43, 13)])) assert transactionPool.receive_transaction(blockchain, transaction)
def test_pick_best_transactions(transactionPool: TransactionPool, blockchain: Blockchain): transactions = mapv(to_transaction, (((63, [(63, 1)])), (63, [(63, 9)]), (63, [(63, 27)]))) for i in (2, 0, 1): transactionPool.receive_transaction(blockchain, transactions[i]) picked_transactions = transactionPool.get_best_transactions(blockchain, 3) indexes = mapv(lambda trans: transactions.index(trans), picked_transactions) assert indexes == [0, 1, 2]
def __init__(self, genesis_block): utxo_pool = UTXOPool() self.tx_pool = TransactionPool() block_plus_to_add = self.BlockPlus(genesis_block, 1, utxo_pool) # hash_content_byte_wrapper = ByteArrayWrapper(genesis_block.hash); hash_content = genesis_block.hash self.block_chain = {} self.block_chain[hash_content] = block_plus_to_add self.last_block_plus = block_plus_to_add # if the first guy gets the coinbase self.add_coinbase_to_UTXOPool(genesis_block, utxo_pool)
def __init__(self, my_port, node_host=None, node_port=None): self.my_ip = self.__get_myip() print('Server IP address is set to ... ', self.my_ip) self.my_port = my_port self.cm = ConnectionManager(self.my_ip, self.my_port, self.__handle_message) self.node_host = node_host self.node_port = node_port self.bm = BlockchainManager() self.tp = TransactionPool() self.clock = 0 self.status = STATUS_IDLE self.next_status = STATUS_IDLE self.new_txs = [] self.end_mining_clock = None self.new_block = None self.to_port = None
class TestTransactionPool(unittest.TestCase): def setUp(self): self.tp = TransactionPool() self.wallet = Wallet() self.recipient = 'r3co09ow99' self.transaction = Transaction.new_transaction(self.wallet, self.recipient, 30) self.tp.update_or_add(self.transaction) def test_add_transaction_to_pool(self): self.assertEqual(self.tp.transactions[0].id, self.transaction.id) def test_update_pool(self): old = str(self.transaction.__dict__) new = self.transaction.insert_output(self.wallet, 'k5co09ow99', 10) self.tp.update_or_add(new) self.assertNotEqual(old, str(self.tp.transactions[0].__dict__)) def test_json_encode(self): print(self.tp.to_json())
help='web server port', default=5000, type=int) parser.add_argument('p2p_port', help='p2p server port', default=6000, type=int) parser.add_argument( '-k', '--key_location', help='location of wallet private key (defaults to "wallet/pk.pem")', default='wallet/pk.pem', type=str) args = parser.parse_args() tx_pool = TransactionPool() blockchain = Blockchain(tx_pool) wallet = Wallet(args.key_location) p2p_application = P2PApplication(blockchain) blockchain.p2p_application = p2p_application web_app.blockchain = blockchain web_app.p2p_application = p2p_application web_app.wallet = wallet print('My pubblic address is: {}'.format( bytes_to_hex(wallet.get_public_key()))) server_url = 'ws://127.0.0.1:{}'.format(args.p2p_port) print('Starting p2p server at {}'.format(server_url)) p2p_application.start_server(server_url)
def test_transaction_wrong(transactionPool: TransactionPool, blockchain: Blockchain): transaction = to_transaction((63, [(43, 29)])) assert not transactionPool.receive_transaction(blockchain, transaction)
def transactionPool() -> TransactionPool: transactionPool = TransactionPool() return transactionPool
class BlockChain: CUT_OFF_AGE = 10 class BlockPlus: def __init__(self, block, index, utxo_pool): self.block = block self.index = index self.utxo_pool = utxo_pool @property def utxo_pool(self): return self.__utxo_pool @utxo_pool.setter def utxo_pool(self, val): self.__utxo_pool = val def get_utxo_pool_copy(self): return UTXOPool(self.utxo_pool) def __init__(self, genesis_block): utxo_pool = UTXOPool() self.tx_pool = TransactionPool() block_plus_to_add = self.BlockPlus(genesis_block, 1, utxo_pool) # hash_content_byte_wrapper = ByteArrayWrapper(genesis_block.hash); hash_content = genesis_block.hash self.block_chain = {} self.block_chain[hash_content] = block_plus_to_add self.last_block_plus = block_plus_to_add # if the first guy gets the coinbase self.add_coinbase_to_UTXOPool(genesis_block, utxo_pool) def get_max_height_block(self): return self.last_block_plus.block def get_max_height_UTXOPool(self): return self.last_block_plus.get_utxo_pool_copy() def get_transaction_pool(self): return self.tx_pool def add_block(self, block): # check for parent block hash prev_block_hash = block.prev_block_hash if prev_block_hash is None: return False # check for parent block # parent_block_plus = self.block_chain.get(ByteArrayWrapper(prev_block_hash)) parent_block_plus = self.block_chain.get(prev_block_hash) if parent_block_plus is None: return False # validate transactions in current block wrt to its parent block's utxoPool (unspent at parents time can only be spent this time) tx_handler = TxHandler(parent_block_plus.get_utxo_pool_copy()) current_txs = block.txs # check all the currentTxs are valid # Note: handleTxs() also updates the corresponding utxoPool valid_txs = tx_handler.handle_txs(current_txs) if len(current_txs) != len(valid_txs): return False # Looks all good, we can add the block now if it has not reached the cut_off # should not be older than CUT_OFF_AGE from last_block_plus if (parent_block_plus.index + 1 <= self.last_block_plus.index - BlockChain.CUT_OFF_AGE): return False utxo_pool = tx_handler.get_utxo_pool() # update utxo_pool for coinbase tx self.add_coinbase_to_utxo_pool(block, utxo_pool) block_plus_to_add = self.BlockPlus(block, parent_block_plus.index + 1, utxo_pool) # self.block_chain[ByteArrayWrapper(block.hashcode)] = block_plus_to_add self.block_chain[block.hashcode] = block_plus_to_add if parent_block_plus.index + 1 > self.last_block_plus.index: self.last_block_plus = block_plus_to_add return True def add_coinbase_to_utxo_pool(self, block, utxo_pool): tx = block.coinbase for i, op in enumerate(tx.outputs): utxo = UTXO(tx.hashcode, i) utxo_pool.append(utxo, op) def add_transaction(self, tx): self.tx_pool.add_transaction(tx)
def setUp(self): self.wallet = Wallet() self.tp = TransactionPool() self.send_amount = 50 self.recipient = 'r3co09ow99'
class Node: def __init__(self, my_port, node_host=None, node_port=None): self.my_ip = self.__get_myip() print('Server IP address is set to ... ', self.my_ip) self.my_port = my_port self.cm = ConnectionManager(self.my_ip, self.my_port, self.__handle_message) self.node_host = node_host self.node_port = node_port self.bm = BlockchainManager() self.tp = TransactionPool() self.clock = 0 self.status = STATUS_IDLE self.next_status = STATUS_IDLE self.new_txs = [] self.end_mining_clock = None self.new_block = None self.to_port = None def start(self): self.cm.start() def reset(self): self.bm.clear_chain() self.tp.clear() self.clock = 0 self.status = STATUS_IDLE self.next_status = STATUS_IDLE def work(self, clock): self.clock = clock self.status = self.next_status if self.status == STATUS_MINING: self.__mine() elif self.status == STATUS_BROADCASTED_BLOCK: self.__broadcast_block() elif self.status == STATUS_BROADCASTED_TX: self.__broadcast_tx() elif self.status == STATUS_RECEIVED_BLOCK: self.__receive_block() elif self.status == STATUS_RECEIVED_TX: self.__receive_tx() elif self.status == STATUS_REQUEST_CHAIN: self.__receive_tx() elif self.status == STATUS_SENT_CHAIN: self.__receive_tx() elif self.status == STATUS_RECEIVED_CHAIN: self.__receive_tx() def __mine(self): if self.end_mining_clock is None: self.end_mining_clock = self.clock + 2 self.new_block = self.__generate_block_with_tp() print("new block: ", self.new_block) self.next_status = STATUS_MINING elif self.end_mining_clock > self.clock: self.next_status = STATUS_MINING else: self.bm.set_new_block(self.new_block) self.tp.clear() self.end_mining_clock = None self.next_status = STATUS_BROADCASTED_BLOCK def __broadcast_block(self): self.cm.broadcast_block(self.new_block) self.new_block = None self.next_status = STATUS_IDLE def __broadcast_tx(self): self.cm.broadcast_tx(self.new_txs) self.new_txs = [] self.next_status = STATUS_IDLE def __receive_block(self): self.bm.set_new_block(self.new_block) self.next_status = STATUS_BROADCASTED_BLOCK def __receive_tx(self): self.tp.set_tx(self.new_txs) self.next_status = STATUS_BROADCASTED_TX def __request_chain(self): self.cm.request_chain(self.to_port) self.to_port = None self.next_status = STATUS_IDLE def __send_chain(self): chain_json = self.bm.get_chain_json() self.cm.send_chain(chain_json, self.to_port) def __received_chain(self): self.next_status = STATUS_IDLE def __generate_block_with_tp(self): txs = self.tp.get_stored_transaction() if txs: return self.bm.generate_new_block(txs) def add_peer(self, peer): self.cm.add_peer((peer['addr'], peer['port'])) def clear_peer(self, peer): self.cm.clear_peer((peer['addr'], peer['port'])) def __handle_message(self, msg): if msg[0] == MSG_NEW_BLOCK: block_dict = msg[2] if block_dict['id'] + 1 <= len(self.bm.chain): print("received known block") self.next_status = STATUS_IDLE else: block = Block(block_dict['id'], block_dict['transaction'], block_dict['previous_block_hash'], block_dict['nonce'], block_dict['timestamp']) print("received new block", block.to_dict()) if block.previous_block_hash == self.bm.chain[-1].get_hash(): self.new_block = block self.next_status = STATUS_RECEIVED_BLOCK else: self.to_port = msg[1] self.next_status = STATUS_REQUEST_CHAIN elif msg[0] == MSG_NEW_TX: txs = json.loads(msg[2]) for tx in txs: if tx in self.tp.get_stored_transaction(): print("received known tx") else: print("received new tx", tx) self.new_txs.append(tx) self.next_status = STATUS_RECEIVED_TX elif msg[0] == MSG_REQ_CHAIN: self.to_port = msg[1] self.next_status = STATUS_SENT_CHAIN elif msg[0] == MSG_CHAIN: self.bm.clear_chain() chain = msg[2] for block_dict in chain: block = Block(block_dict['id'], block_dict['transaction'], block_dict['previous_block_hash'], block_dict['nonce'], block_dict['timestamp']) self.bm.set_new_block(block) self.next_status = STATUS_RECEIVED_CHAIN def join_network(self): if self.node_host is not None: self.cm.join_network(self.node_host, self.node_port) else: print('This server is running as Genesis Node ...') def get_current_status(self): return self.status def __get_myip(self): s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.connect(('8.8.8.8', 80)) return s.getsockname()[0] def get_status(self): data = json.dumps( { "clock": self.clock, "status": self.status } ).encode("utf-8") return data def get_peers(self): data = json.dumps(self.cm.peers).encode('utf-8') return data def get_latest_block(self): latest_block = self.bm.chain[-1] return latest_block.to_json() def get_block(self, index): chain = self.bm.chain if index < len(chain): return chain[index].to_json() else: return None def get_all_blocks(self): return self.bm.get_chain_json() def get_tx_in_pool(self): txs = self.tp.get_stored_transaction() return json.dumps(txs).encode('utf-8') def set_clock(self, clock): self.clock = clock
def create_app(my_ip: str = 'localhost:5000', key: int = 1, servers: List[str] = []): app = Flask(__name__) servers = deepcopy(servers) blockchain = Blockchain() transactionPool = TransactionPool() miner_key = PrivateKey(key).public_key().to_bytes() def send_my_ip(): server_data = servers + [my_ip] for server in servers: requests.post(f'http://{server}/receiveips', \ data=dumps(server_data)) def send_blockchain(): for server in servers: blockArray = BlockArray(blockchain) blockchain_bytes = b64encode(pickle.dumps(blockArray)) requests.post(f'http://{server}/receiveblockchain', \ data=blockchain_bytes) send_my_ip() @app.route('/blockchain') def blockchain_web(): return jsonify(blockchain.to_dict()) @app.route('/addtransaction', methods=['POST']) def add_transaction(): transaction_dict = request.json form = TransactionForm(**transaction_dict) if form.validate(): wallet = Wallet(key) transaction = Transaction(outputs=[(PrivateKey( transaction_dict['receiver']).public_key().to_bytes(), transaction_dict['amount'])]) signed_transaction = wallet.sign(transaction) transactionPool.receive_transaction(blockchain, signed_transaction) return '' return '', 403 @app.route('/mine') def mine(): if len(blockchain) == 0: genesis_block = GenesisBlock(miner_pub_key=miner_key) genesis_block.mine(blockchain.get_difficult()) blockchain.add(genesis_block) else: block = Block(previous_block=blockchain.chain[-1], miner_pub_key=miner_key, \ transactions=transactionPool.get_best_transactions(blockchain,1)) block.mine(blockchain.get_difficult()) blockchain.add(block) send_blockchain() return '' @app.route('/receiveblockchain', methods=['POST']) def receive_blockchain(): blockArray = pickle.loads(b64decode(request.data)) if isinstance(blockArray, BlockArray): len_before = len(blockchain) blockchain.substitute(blockArray) len_after = len(blockchain) if len_after > len_before: send_blockchain() return '' return '', 403 @app.route('/receiveips', methods=['POST']) def receive_ips(): ips = loads(request.data.decode('utf-8')) if isinstance(ips, list): for ip in ips: if ip not in servers and ip != my_ip: servers.append(ip) return '' @app.route('/getips') def get_ips(): return jsonify(servers) return app
import logging import threading import base64 from flask import Flask, request, jsonify, redirect from blockchain import Blockchain from p2p import MyOwnPeer2PeerNode from wallet import Wallet from transaction_pool import TransactionPool app = Flask(__name__) bc = Blockchain() wallet = Wallet() tp = TransactionPool() # Use diffierent ports to simulate nodes in my development environment. # It will be distinguish by ip address in real world. HTTP_PORT = os.environ[ "HTTP_PORT"] if 'HTTP_PORT' in os.environ else 3000 # export HTTP_PORT=3001 P2P_PORT = int(os.environ["P2P_PORT"] ) if 'P2P_PORT' in os.environ else 5001 # export P2P_PORT=5002 node = MyOwnPeer2PeerNode("127.0.0.1", int(P2P_PORT), bc) @app.route('/blocks') def blocks(): j = bc.to_json() return j