Compare commits
No commits in common. "f9f6265afbade2af9f1d5fc0a6a53cb9f8037bbf" and "c9c37849c9173e9888a93a5d7f19dd6399fc142e" have entirely different histories.
f9f6265afb
...
c9c37849c9
2
.gitignore
vendored
2
.gitignore
vendored
@ -169,7 +169,6 @@ cython_debug/
|
|||||||
# Icon must end with two \r
|
# Icon must end with two \r
|
||||||
Icon
|
Icon
|
||||||
|
|
||||||
|
|
||||||
# Thumbnails
|
# Thumbnails
|
||||||
._*
|
._*
|
||||||
|
|
||||||
@ -189,4 +188,3 @@ Network Trash Folder
|
|||||||
Temporary Items
|
Temporary Items
|
||||||
.apdisk
|
.apdisk
|
||||||
|
|
||||||
.idea
|
|
@ -1,163 +0,0 @@
|
|||||||
# 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
163
DPOS/dpos_api.py
@ -1,163 +0,0 @@
|
|||||||
# 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)
|
|
@ -1,115 +0,0 @@
|
|||||||
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
116
POW/pow_api.py
@ -1,116 +0,0 @@
|
|||||||
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