Compare commits

...

5 Commits

Author SHA1 Message Date
myh
f9f6265afb DPOS 共识算法API接口实现 2024-05-29 18:34:36 +08:00
myh
93ae02916d POW 共识算法API接口实现 2024-05-29 18:34:26 +08:00
myh
8379de4870 DPOS 共识算法实现 2024-05-29 18:34:06 +08:00
myh
00fcf3962c POW 共识算法实现 2024-05-29 18:33:57 +08:00
myh
3979bfd2ac 忽略 .idea 文件夹 2024-05-29 18:33:30 +08:00
5 changed files with 560 additions and 1 deletions

4
.gitignore vendored
View File

@ -167,7 +167,8 @@ cython_debug/
.LSOverride
# Icon must end with two \r
Icon
Icon
# Thumbnails
._*
@ -188,3 +189,4 @@ Network Trash Folder
Temporary Items
.apdisk
.idea

163
DPOS/blockChain.py Normal file
View 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
View 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
View 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
View 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)