diff --git a/DPOS/dpos_api.py b/DPOS/dpos_api.py new file mode 100644 index 0000000..34d2f90 --- /dev/null +++ b/DPOS/dpos_api.py @@ -0,0 +1,163 @@ +# Interacting with the Dexter's blockchain with multiple nodes using HTTP requests + +from flask import Flask, jsonify, request + +from blockChain import Blockchain + +# Initialise our node with identifier and instantiate the Blockchain class +app = Flask(__name__) +blockchain = Blockchain() + + +# API endpoint to mine a block, it is an HTTP GET request +@app.route('/mine', methods=['GET']) +# Method to mine a block +def mine(): + # To ensure that only delegates elected by voting can mine a new block + current_port = "localhost:" + str(port) + if current_port in blockchain.delegates: + + # To ensure that a new block is mined only if there are atleast 2 transactions + if len(blockchain.unverified_transactions) >= 2: + last_block = blockchain.last_block + previous_hash = blockchain.hash(last_block) + block = blockchain.new_block(previous_hash) + + response = { + 'message': "New block mined!", + 'index': block['index'], + 'transactions': block['transactions'], + 'previous_hash': block['previous_hash'] + } + print(len(blockchain.unverified_transactions)) + return jsonify(response), 200 + + else: + response = { + 'message': 'Not enough transactions to mine a new block and add to chain!' + } + print(len(blockchain.unverified_transactions)) + return jsonify(response), 400 + else: + response = { + 'message': 'You are not authorised to mine block! Only delegates can mine.' + } + return jsonify(response), 400 + + +# Endpoint for a new transaction +@app.route('/transactions/new', methods=['POST']) +def new_transaction(): + values = request.get_json() + + required = ['Customer name', 'Item name', 'Total billing amount'] + if not all(k in values for k in required): + return 'Missing values! Please enter customer name, item name and billing amount.', 400 + + index = blockchain.new_transaction(values['Customer name'], values['Item name'], values['Total billing amount']) + + response = { + 'message': f'Transaction will be added to block {index}' + } + return jsonify(response), 201 + + +# Endpoint for viewing the blockchain +@app.route('/chain', methods=['GET']) +def full_chain(): + response = { + 'chain': blockchain.chain, + 'length': len(blockchain.chain) + } + return jsonify(response), 200 + + +# Endpoint for adding HTTP address of new nodes along with their stakes in the network. +@app.route('/nodes/add', methods=['POST']) +def add_nodes(): + values = request.get_json() + required = ['nodes', 'stake'] + + if not all(k in values for k in required): + return 'Error', 400 + + blockchain.add_node(values['nodes'], values['stake']) + + response = { + 'message': 'New nodes are added!', + 'total_nodes': list(blockchain.nodes) + } + print(blockchain.nodes) + return jsonify(response), 201 + + +# Endpoint to start the voting process +@app.route('/voting', methods=['GET']) +def voting(): + if port == 5000: + show_votes = blockchain.add_vote() + + response = { + 'message': 'The voting results are as follows:', + 'nodes': blockchain.voteNodespool + } + + return jsonify(response), 200 + + else: + response = { + 'message': 'You are not authorized to conduct the election process!' + } + return jsonify(response), 400 + + +# Endpoint to view the list of all three elected delegate nodes +@app.route('/delegates/show', methods=['GET']) +def delegates(): + show_delegates = blockchain.selection() + + response = { + 'message': 'The 3 delegate nodes selected for block mining are:', + 'node_delegates': blockchain.delegates + } + return jsonify(response), 200 + + +# Endpoint to synchronise the list of elected delegates with all other nodes in the network +@app.route('/delegates/sync', methods=['GET']) +def sync_delegates(): + sync_delegates = blockchain.sync() + + response = { + 'message': 'The delegate nodes are:', + 'node_delegates': blockchain.delegates + } + return jsonify(response), 200 + + +# Endpoint to resolve and replace current chain with the longest validated one,achieving consensus +@app.route('/chain/resolve', methods=['GET']) +def consensus(): + replaced = blockchain.resolve_chain() + + if replaced: + response = { + 'message': 'Our chain was replaced', + 'new_chain': blockchain.chain + } + else: + response = { + 'message': 'Our chain is authoritative', + 'chain': blockchain.chain + } + return jsonify(response), 200 + + +if __name__ == '__main__': + from argparse import ArgumentParser + + parser = ArgumentParser() + parser.add_argument('-p', '--port', default=5000, type=int, help='Listening on port') + args = parser.parse_args() + port = args.port + app.run(host='0.0.0.0', port=port)