Compare commits
5 Commits
c9c37849c9
...
f9f6265afb
Author | SHA1 | Date | |
---|---|---|---|
f9f6265afb | |||
93ae02916d | |||
8379de4870 | |||
00fcf3962c | |||
3979bfd2ac |
4
.gitignore
vendored
4
.gitignore
vendored
@ -167,7 +167,8 @@ cython_debug/
|
|||||||
.LSOverride
|
.LSOverride
|
||||||
|
|
||||||
# Icon must end with two \r
|
# Icon must end with two \r
|
||||||
Icon
|
Icon
|
||||||
|
|
||||||
|
|
||||||
# Thumbnails
|
# Thumbnails
|
||||||
._*
|
._*
|
||||||
@ -188,3 +189,4 @@ Network Trash Folder
|
|||||||
Temporary Items
|
Temporary Items
|
||||||
.apdisk
|
.apdisk
|
||||||
|
|
||||||
|
.idea
|
163
DPOS/blockChain.py
Normal file
163
DPOS/blockChain.py
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
# Implementation of a simple decentralized blockchain class with multiple nodes and DPoS consensus in the network
|
||||||
|
|
||||||
|
import hashlib
|
||||||
|
import json
|
||||||
|
from datetime import datetime
|
||||||
|
from urllib.parse import urlparse
|
||||||
|
import requests
|
||||||
|
from random import randint
|
||||||
|
|
||||||
|
|
||||||
|
# Blockchain class
|
||||||
|
class Blockchain(object):
|
||||||
|
|
||||||
|
# Constructor which creates lists to store the blockchain and the transactions
|
||||||
|
def __init__(self):
|
||||||
|
|
||||||
|
# List to store the blockchain
|
||||||
|
self.chain = []
|
||||||
|
|
||||||
|
# List to store the unverified transactions
|
||||||
|
self.unverified_transactions = []
|
||||||
|
|
||||||
|
# List to store verified transactions
|
||||||
|
self.verified_transactions = []
|
||||||
|
|
||||||
|
# Genesis block
|
||||||
|
self.new_block(previous_hash=1)
|
||||||
|
|
||||||
|
# Set containing the nodes in the network. Used set here to prevent the same node getting added again.
|
||||||
|
self.nodes = set()
|
||||||
|
|
||||||
|
# List containing all the nodes along with their stake in the network
|
||||||
|
self.all_nodes = []
|
||||||
|
|
||||||
|
# List of all the voting nodes in the network
|
||||||
|
self.voteNodespool = []
|
||||||
|
|
||||||
|
# List which stores all the nodes in descending order of votes received
|
||||||
|
self.starNodespool = []
|
||||||
|
|
||||||
|
# List to store the top 3 nodes with the highest (stake * votes_received)
|
||||||
|
self.superNodespool = []
|
||||||
|
|
||||||
|
# List to store the address of the delegate nodes selected for mining process
|
||||||
|
self.delegates = []
|
||||||
|
|
||||||
|
# Method to create a new block in the Blockchain
|
||||||
|
def new_block(self, previous_hash=None):
|
||||||
|
block = {
|
||||||
|
'index': len(self.chain) + 1,
|
||||||
|
'timestamp': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
|
||||||
|
'transactions': self.unverified_transactions,
|
||||||
|
'previous_hash': previous_hash or self.hash(self.chain[-1])
|
||||||
|
}
|
||||||
|
self.verified_transactions += self.unverified_transactions
|
||||||
|
print(self.verified_transactions)
|
||||||
|
self.unverified_transactions = []
|
||||||
|
|
||||||
|
# appending the block at the end of the blockchain
|
||||||
|
self.chain.append(block)
|
||||||
|
return block
|
||||||
|
|
||||||
|
# Method to add a new transaction in the next block
|
||||||
|
def new_transaction(self, sender, item_name, bill_amount):
|
||||||
|
self.unverified_transactions.append({
|
||||||
|
'Customer name': sender,
|
||||||
|
'Recipient': "Dexter's Coffee Shop",
|
||||||
|
'Item name': item_name,
|
||||||
|
'Total billing amount': bill_amount,
|
||||||
|
'timestamp': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||||
|
})
|
||||||
|
return self.last_block['index'] + 1
|
||||||
|
|
||||||
|
@property
|
||||||
|
def last_block(self):
|
||||||
|
return self.chain[-1]
|
||||||
|
|
||||||
|
# Static method to create an SHA-256 Hash of a given block
|
||||||
|
@staticmethod
|
||||||
|
def hash(block):
|
||||||
|
block_string = json.dumps(block, sort_keys=True).encode()
|
||||||
|
hash_val = hashlib.sha256(block_string).hexdigest()
|
||||||
|
return hash_val
|
||||||
|
|
||||||
|
# Method to add node using its IP address to our Blockchain network.
|
||||||
|
def add_node(self, address, stake):
|
||||||
|
parsed_url = urlparse(address)
|
||||||
|
authority = stake
|
||||||
|
self.nodes.add((parsed_url.netloc, authority))
|
||||||
|
|
||||||
|
# Method to simulate the voting process
|
||||||
|
def add_vote(self):
|
||||||
|
self.all_nodes = list(self.nodes)
|
||||||
|
|
||||||
|
for x in self.all_nodes:
|
||||||
|
y = list(x)
|
||||||
|
y.append(x[1] * randint(0, 100))
|
||||||
|
self.voteNodespool.append(y)
|
||||||
|
|
||||||
|
print(self.voteNodespool)
|
||||||
|
|
||||||
|
# Method to select top three nodes based on voting results
|
||||||
|
def selection(self):
|
||||||
|
self.starNodespool = sorted(self.voteNodespool, key=lambda vote: vote[2], reverse=True)
|
||||||
|
print(self.starNodespool)
|
||||||
|
|
||||||
|
for x in range(3):
|
||||||
|
self.superNodespool.append(self.starNodespool[x])
|
||||||
|
print(self.superNodespool)
|
||||||
|
|
||||||
|
for y in self.superNodespool:
|
||||||
|
self.delegates.append(y[0])
|
||||||
|
print(self.delegates)
|
||||||
|
|
||||||
|
# Method to sync the list
|
||||||
|
def sync(self):
|
||||||
|
r = requests.get('http://localhost:5000/delegates/show')
|
||||||
|
print(r)
|
||||||
|
|
||||||
|
if r.status_code == 200:
|
||||||
|
delegates = r.json()['node_delegates']
|
||||||
|
self.delegates = delegates[0:3]
|
||||||
|
print(self.delegates)
|
||||||
|
|
||||||
|
# Method to check if the chain is validated.
|
||||||
|
def valid_chain(self, chain):
|
||||||
|
last_block = chain[0]
|
||||||
|
current_index = 1
|
||||||
|
|
||||||
|
while current_index < len(chain):
|
||||||
|
block = chain[current_index]
|
||||||
|
|
||||||
|
# If the hash value of the current block isn't correct then return false
|
||||||
|
if block['previous_hash'] != self.hash(last_block):
|
||||||
|
return False
|
||||||
|
|
||||||
|
last_block = block
|
||||||
|
current_index += 1
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
# Method to replace the blockchain with the longest validated chain in the network.
|
||||||
|
def resolve_chain(self):
|
||||||
|
neighbours = self.nodes
|
||||||
|
new_chain = None
|
||||||
|
max_length = len(self.chain)
|
||||||
|
|
||||||
|
for node in neighbours:
|
||||||
|
response = requests.get(f'http://{node}/chain')
|
||||||
|
|
||||||
|
if response.status_code == 200:
|
||||||
|
length = response.json()['length']
|
||||||
|
chain = response.json()['chain']
|
||||||
|
|
||||||
|
if length > max_length and self.valid_chain(chain):
|
||||||
|
max_length = length
|
||||||
|
new_chain = chain
|
||||||
|
|
||||||
|
if new_chain:
|
||||||
|
self.chain = new_chain
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
163
DPOS/dpos_api.py
Normal file
163
DPOS/dpos_api.py
Normal file
@ -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)
|
115
POW/blockChain.py
Normal file
115
POW/blockChain.py
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
import hashlib
|
||||||
|
import requests
|
||||||
|
import json
|
||||||
|
from hashlib import sha256
|
||||||
|
from time import time
|
||||||
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
|
|
||||||
|
class Blockchain(object):
|
||||||
|
def __init__(self):
|
||||||
|
self.chain = [{'index': 0,
|
||||||
|
'timestamp': time(),
|
||||||
|
'transactions': 0,
|
||||||
|
'proof': 0,
|
||||||
|
'previous_hash': 0}]
|
||||||
|
self.current_transactions = []
|
||||||
|
self.nodes = set()
|
||||||
|
|
||||||
|
# create a new node based on url
|
||||||
|
def register_node(self, address):
|
||||||
|
parsed_url = urlparse(address)
|
||||||
|
self.nodes.add(parsed_url.netloc)
|
||||||
|
|
||||||
|
# create a new block and add it to the chain.
|
||||||
|
def new_block(self, proof, previous_hash):
|
||||||
|
block = {
|
||||||
|
'index': len(self.chain) + 1,
|
||||||
|
'timestamp': time(),
|
||||||
|
'transactions': self.current_transactions,
|
||||||
|
'proof': proof,
|
||||||
|
'previous_hash': previous_hash or self.hash(self.chain[-1])
|
||||||
|
}
|
||||||
|
|
||||||
|
self.current_transactions = []
|
||||||
|
|
||||||
|
self.chain.append(block)
|
||||||
|
return block
|
||||||
|
|
||||||
|
# add a new transaction to the list of transactions.
|
||||||
|
def new_transaction(self, sender, recipient, amount):
|
||||||
|
self.current_transactions.append({
|
||||||
|
'sender': sender,
|
||||||
|
'recipient': recipient,
|
||||||
|
'amount': amount
|
||||||
|
})
|
||||||
|
|
||||||
|
return self.last_block['index'] + 1
|
||||||
|
|
||||||
|
# PoW algo that increments proof value until valid_proof returns true.
|
||||||
|
def proof_of_work(self, last_proof):
|
||||||
|
proof = 0
|
||||||
|
while self.valid_proof(last_proof, proof) is False:
|
||||||
|
proof += 1
|
||||||
|
|
||||||
|
return proof
|
||||||
|
|
||||||
|
# verify the current chain has not already been solved
|
||||||
|
def valid_chain(self, chain):
|
||||||
|
last_block = chain[0]
|
||||||
|
current_index = 1
|
||||||
|
|
||||||
|
while current_index < len(chain):
|
||||||
|
block = chain[current_index]
|
||||||
|
if block['previous_hash'] != self.hash(last_block):
|
||||||
|
return False
|
||||||
|
|
||||||
|
# verify values in the chain are correct
|
||||||
|
if not self.valid_proof(last_block['proof'], block['proof']):
|
||||||
|
return False
|
||||||
|
|
||||||
|
last_block = block
|
||||||
|
current_index += 1
|
||||||
|
return True
|
||||||
|
|
||||||
|
# consensus algo to prevent conflicts when multiple nodes are in a network
|
||||||
|
def resolve_conflicts(self):
|
||||||
|
neighbors = self.nodes
|
||||||
|
new_chain = None
|
||||||
|
max_length = len(self.chain)
|
||||||
|
|
||||||
|
# check each node to find the longest chain
|
||||||
|
for node in neighbors:
|
||||||
|
response = requests.get(f'http://{node}/chain')
|
||||||
|
|
||||||
|
if response.status_code == 200:
|
||||||
|
length = response.json()['length']
|
||||||
|
chain = response.json()['chain']
|
||||||
|
|
||||||
|
if length > max_length and self.valid_chain(chain):
|
||||||
|
max_length = length
|
||||||
|
new_chain = chain
|
||||||
|
if new_chain:
|
||||||
|
self.chain = new_chain
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
# checks if the hash has 4 leading zeros. returns boolean.
|
||||||
|
@staticmethod
|
||||||
|
def valid_proof(last_proof, proof):
|
||||||
|
guess = f'{last_proof}{proof}'.encode()
|
||||||
|
guess_hash = hashlib.sha256(guess).hexdigest()
|
||||||
|
# the addition of leading zeros can make a massive difference to the time required to find a solution.
|
||||||
|
return guess_hash[:4] == "0000"
|
||||||
|
|
||||||
|
# hashes a block
|
||||||
|
@staticmethod
|
||||||
|
def hash(block):
|
||||||
|
block_string = json.dumps(block, sort_keys=True).encode()
|
||||||
|
return sha256(block_string).hexdigest()
|
||||||
|
|
||||||
|
# returns the last block of the chain
|
||||||
|
@property
|
||||||
|
def last_block(self):
|
||||||
|
return self.chain[-1]
|
116
POW/pow_api.py
Normal file
116
POW/pow_api.py
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
from flask import Flask, jsonify, request
|
||||||
|
from uuid import uuid4
|
||||||
|
from blockChain import Blockchain
|
||||||
|
|
||||||
|
# Instantiate our Node
|
||||||
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
# Generate a globally unique address for this node
|
||||||
|
node_identifier = str(uuid4()).replace('-', '')
|
||||||
|
|
||||||
|
# Instantiate the Blockchain
|
||||||
|
blockchain = Blockchain()
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/mine', methods=['GET'])
|
||||||
|
def mine():
|
||||||
|
# run the PoW algo
|
||||||
|
last_block = blockchain.last_block
|
||||||
|
last_proof = last_block['proof']
|
||||||
|
proof = blockchain.proof_of_work(last_proof)
|
||||||
|
|
||||||
|
# get reward for solving a block
|
||||||
|
blockchain.new_transaction(sender="0", recipient=node_identifier, amount=1)
|
||||||
|
|
||||||
|
# add a new block to the chain
|
||||||
|
previous_hash = blockchain.hash(last_block)
|
||||||
|
block = blockchain.new_block(proof, previous_hash)
|
||||||
|
|
||||||
|
# create response
|
||||||
|
response = {
|
||||||
|
'message': "New block forged",
|
||||||
|
'index': block['index'],
|
||||||
|
'transactions': block['transactions'],
|
||||||
|
'proof': block['proof'],
|
||||||
|
'previous_hash': block['previous_hash']
|
||||||
|
}
|
||||||
|
|
||||||
|
# return response
|
||||||
|
return jsonify(response), 200
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/transactions/new', methods=['POST'])
|
||||||
|
def new_transaction():
|
||||||
|
# get values from request
|
||||||
|
values = request.get_json()
|
||||||
|
|
||||||
|
# verify all values are present
|
||||||
|
required = ['sender', 'recipient', 'amount']
|
||||||
|
if not all(k in values for k in required):
|
||||||
|
return "Missing values", 400
|
||||||
|
|
||||||
|
# create a new transaction
|
||||||
|
index = blockchain.new_transaction(values['sender'], values['recipient'], values['amount'])
|
||||||
|
|
||||||
|
# create and return response
|
||||||
|
response = {'message': f'Transaction will be added to Block {index}'}
|
||||||
|
return jsonify(response), 200
|
||||||
|
|
||||||
|
|
||||||
|
# return the full chain
|
||||||
|
@app.route('/chain', methods=['GET'])
|
||||||
|
def full_chain():
|
||||||
|
response = {
|
||||||
|
'chain': blockchain.chain,
|
||||||
|
'length': len(blockchain.chain),
|
||||||
|
}
|
||||||
|
return jsonify(response), 200
|
||||||
|
|
||||||
|
|
||||||
|
# create a new node
|
||||||
|
@app.route('/nodes/register', methods=['POST'])
|
||||||
|
def register_nodes():
|
||||||
|
values = request.get_json()
|
||||||
|
|
||||||
|
nodes = values.get('nodes')
|
||||||
|
if nodes is None:
|
||||||
|
return "Error: Please supply a valid list of nodes", 400
|
||||||
|
|
||||||
|
for node in nodes:
|
||||||
|
blockchain.register_node(node)
|
||||||
|
|
||||||
|
response = {
|
||||||
|
'message': 'New nodes have been added',
|
||||||
|
'total_nodes': list(blockchain.nodes),
|
||||||
|
}
|
||||||
|
return jsonify(response), 201
|
||||||
|
|
||||||
|
|
||||||
|
# consensus algo
|
||||||
|
@app.route('/nodes/resolve', methods=['GET'])
|
||||||
|
def consensus():
|
||||||
|
replaced = blockchain.resolve_conflicts()
|
||||||
|
|
||||||
|
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__':
|
||||||
|
# app.run(host='0.0.0.0', port=5000)
|
||||||
|
from argparse import ArgumentParser
|
||||||
|
|
||||||
|
parser = ArgumentParser()
|
||||||
|
parser.add_argument('-p', '--port', default=6000, type=int, help='Listening on port')
|
||||||
|
args = parser.parse_args()
|
||||||
|
port = args.port
|
||||||
|
app.run(host='0.0.0.0', port=port)
|
Loading…
Reference in New Issue
Block a user