{Ανάπτυξη Ασφαλών Συστημάτων}

Προγραμματιστική Εργασία

{ Δημιουργία Περιβάλλοντος Blockchain }

Αυγέρης Τσιρώνης – Α.Μ. 00909

atsironis@uth.gr

Περιεχόμενα

α. Δημιουργία Ιδιωτικού – Δημόσιου Κλειδιού

β. Υπογραφή

γ. Έλεγχος εγκυρότητας

δ. Δημιουργία αρχιτεκτονικής του Blockchain

1.

2.

α. Δημιουργία Κρυπτονομίσματος

β. Σύνδεση με HTTP Client

3.

   Εκτέλεση Εφαρμογής

 

# CHAPTER 2

1

Κατά την εκκίνηση εκχωρείται ένα ιδιωτικό κλειδί (χωρίς δυνατότητα κοινής χρήσης) και αυτό είναι το μοναδικό αναγνωριστικό του χρήστη

2

Από το ιδιωτικό κλειδί δημιουργείται ένα δημόσιο κλειδί (κοινής χρήσης). Χρησιμοποιείται για την αποστολή χρημάτων και για την επαλήθευση  της συναλλαγής

3

Η συναλλαγή περιέχει τον αποστολέα (sender's public key), τον παραλήπτη (receiver’s public key) και το ποσό της συναλλαγής. Το σύνολο των περιεχομένων της συναλλαγής νοείται ως ένα μήνυμα (message).

5

Η επαλήθευση πραγματοποιείται χρησιμοποιόντας το δημόσιο κλειδί, το μήνυμα και την υπογραφή.

4

Ιδιωτικό κλειδί και μήνυμα συνδυάζονται για να δημιουργήσουν μία υπογραφή.  

Δημιουργία Ιδιωτικού και Δημόσιου Κλειδιού

Δημιουργία Ιδιωτικού Κλειδιού

from cryptography.hazmat.backends import default_backend 
from cryptography.hazmat.primitives.asymmetric import rsa 
from cryptography.hazmat.primitives import hashes 
from cryptography.hazmat.primitives.asymmetric import padding 
from cryptography.hazmat.primitives import serialization
from cryptography.exceptions import InvalidSignature

question = input("Do you have a private key? Y/N\n")
if question == "N" or question == "n":
    private_key = rsa.generate_private_key(
        public_exponent=65537, 
        key_size=2048, #bits
        backend=default_backend()) #get the default backend
    
    pr = private_key
#pem file creation for future use. The node should ender the app with the same private and public key always. Pem files encode the private and public keys
    pem = pr.private_bytes(
        encoding=serialization.Encoding.PEM,
        format=serialization.PrivateFormat.TraditionalOpenSSL,
        encryption_algorithm=serialization.NoEncryption()) 
        
    with open("private_key.pem", "wb") as f:
               f.write(pem)
                         
    public_key = private_key.public_key()

    pr = pem.decode('utf-8')
    pu = public_key
    
    pem2 = pu.public_bytes(
        encoding=serialization.Encoding.PEM,
        format=serialization.PublicFormat.SubjectPublicKeyInfo)
    
    pu = pem2.decode('utf-8')
    
    with open("public_key.pem", "wb") as f:
               f.write(pem2)
#if there is already a private key we load the pem files and we decode them             
else:
    with open("private_key.pem", "rb") as key_file:
        private_key = serialization.load_pem_private_key(key_file.read(), password = None)
        
        
    with open("public_key.pem", "rb") as file:
        public_key = serialization.load_pem_public_key(file.read())
    pr = private_key
    pem = pr.private_bytes(
        encoding=serialization.Encoding.PEM,
        format=serialization.PrivateFormat.TraditionalOpenSSL,
        encryption_algorithm=serialization.NoEncryption())
    pr = pem.decode('utf-8')
    pu = public_key

    with open("private_key2.pem", "wb") as f:
               f.write(pem)
    pem2 = public_key.public_bytes(
        encoding=serialization.Encoding.PEM,
        format=serialization.PublicFormat.SubjectPublicKeyInfo)
    with open("public_key2.pem", "wb") as f:
               f.write(pem2)           
    pu = pem2.decode('utf-8')             
# OrpheumCoin
#Creating Signature
def sign(message, private_key):
    sig = private_key.sign(
        message,
        padding.PSS(
            mgf=padding.MGF1(hashes.SHA256()),
            salt_length=padding.PSS.MAX_LENGTH
        ),
        hashes.SHA256()
    )
    return sig
# OrheumCoin CODE

Υπογραφή

def verify (message, sig, public_key):
    try:
        public_key.verify(
            sig,
            message,
    #we have to pass the same padding - hash
            padding.PSS(
            mgf=padding.MGF1(hashes.SHA256()),
            salt_length=padding.PSS.MAX_LENGTH
            ),
            hashes.SHA256()
        )
        #If it completes  return true  
        return True
    except InvalidSignature:
        print("ERROR!! Invalide sig!!!")   
        return False
    except:
        print("Error executing public.verify")
# OrheumCoin CODE

Έλεγχος Εγκυρότητας

{ Δημιουργία Αρχιτεκτονικής του Blockchain }

  • Αλυσίδα
  • Blocks
  • Nodes
  • Consensus
  • Συναλλαγές

Ορισμός των στοιχείων της Blockchain κλάσης

#Creating a file that it will be our mempool
mempool = []
with open('transactions.json', 'w') as jsonfile:
            json.dump(mempool, jsonfile)

##A. BUILDING THE ARCHTECTURE OF THE BLOCKCHAIN
#Defining all the components of the blockchain
class Blockchain:
  
    def __init__(self):
      	#initialize the chain that will contain the blocks.
        self.chain = [] 
        # list that will contain transactions
        self.transactions = [] 
        #Create the Genesis Block
        self.create_block(proof = 1, previous_hash = '0') 
        #Set containing the nodes
        self.nodes = set()
# OrpheumCoin CODE
import random
import datetime
import hashlib
import json
import os
from flask import Flask, jsonify 
from flask import request
import requests
from uuid import uuid4
from urllib.parse import urlparse
    #Create block function must aplly after mining a block
    def create_block(self, proof, previous_hash): 
    #define the new block by creating a dictionary that will define each block  
        block = {'index': len(self.chain) + 1, 
                 'timestamp': str(datetime.datetime.now()), 
                 'proof': proof,  
                 'previous_hash': previous_hash,
                 'transactions': self.transactions} #
        self.transactions = []
        os.remove('transactions.json')
        with open('transactions.json', 'w') as jsonfile:
                    json.dump(mempool, jsonfile)
        #append the block to the list (blockchain) 
        self.chain.append(block)
        #return this block because  we're going to display the 
        #informations of this block in Postman.
        return block
# OrpheumCoin CODE

Block Creation

    #create a method to get the previous block
    def get_previous_block(self): 
        #last index of the chain
        return self.chain[-1]
# OrpheumCoin CODE

Previous Block

Proof of Work

#PROOF_of_WORK 
def PoW(self, previous_proof): 
    nonce = random.getrandbits(32) 
    #Variable that checks if the new proof is the right one
    check_proof = False
    #Defining the problem that miners have to solve using hash function
    while check_proof is False:
    	#creating a new variable that takes the previous proof and the nonce
    	# this operation should be non symmetrical
    	hash_operation = hashlib.sha256(str(nonce**2 - 
                                            previous_proof**2).encode()).hexdigest() 
        #we put it on a str in order to be acceptable from sha256. Encode function will include the str in the right format for sha256 (adds a b before the str)
    	#define the 2nd part of the problem. 
    	#check if the 5 first char of this has operation are 5 zeros
    	if hash_operation[:5] == '00000':
    	     check_proof = True
		else: 
	    #we add 1 to continue the loop and to check for 5 zeros
			nonce += 1
    return nonce
# OrpheumCoin CODE
#Creating a function that checks if everything is right in the blockchain
#Checking each block for a correct PoW & 
#Check if Prev Hash of each block is equal to the hash of Prev Block
#This function takes a block as input and returns the sha256 cryptographic hash
    def hash(self, block):
        #we exclude The current hash from hashing
        myDict = {}
        myDict.update(block)
        json.dumps(myDict)
        if 'hash' in myDict:
            del myDict['hash']
        encoded_block = json.dumps(myDict, sort_keys = True).encode()
        return hashlib.sha256(encoded_block).hexdigest()
# OrpheumCoin CODE

Εγκυρότητα Αλυσίδας

def is_chain_valid(self, chain):        
	#1. initialize prev_block as the first block of the chain 
	previous_block = chain[0]
	#2. Initialize the looping variable. Each block has the index 
	#key in the dictionary which is the number of the block (starts at 1)
	block_index = 1
	#create a loop to iterate on all the blocks. 
	#while block index is lower than the length of the chain
	while block_index < len(chain):
		#check if prev_hash of Block  = hash of prev_Block
		block = chain[block_index]
		if block['previous_hash'] != self.hash(previous_block):
		return False
		#check proof of each block for validation
		#taking the prev_proof and then the current_proof and 
		#computing the hash operation between prev_proof & current_proof
		#and checking if this hash operation starts with 5 leading zeros
		previous_proof = previous_block['proof']
		proof = block['proof']
		#call the hash operation
		hash_operation = hashlib.sha256(str(proof**2 - 
                                        previous_proof**2).encode()).hexdigest()
		#check if starts with 0000
        if hash_operation[:5] != '00000':
            return False
        #Updating Loop Variable Block index & prev block variable
        previous_block = block
        block_index += 1 # block_index = Block index +1
        return True
# OrpheumCoin CODE

Εγκυρότητα αλυσίδας

##Creating the format of the transactions (sender, receiver, amount of coins)
def add_transaction(self, sender, receiver, amount):  
    with open('transactions.json') as jsonfile:
            pool = json.load(jsonfile)
            self.transactions.append(pool)
        
        self.transactions.append({'sender': sender,
                                  'receiver': receiver,
                                  'amount': amount})
    #append the transaction into pool
    with open('transactions.json', 'w') as jsonfile:
           json.dump(self.transactions, jsonfile)
    #list of transaction returns to empty
    self.transactions=[]
    ##Returning the index of the (new) block that will
    #receive these transactions
    previous_block = self.get_previous_block() 
    ## and then returning the index of the new block 
    #that will enclude the transactions.
    return previous_block['index'] + 1 
# OrpheumCoin CODE

Προσθήκη Συναλλαγών

##Creating consensus algorithm to make sure that all 
#the nodes contain the same chain

#1. Adding Nodes
def add_node(self, address):
    ##parse the address of the node with the url parse function
    parsed_url = urlparse(address) 
    self.nodes.add(parsed_url.netloc)
# OrpheumCoin CODE

Προσθήκη Κόμβων

    ##Creating a method that replaces any chain that is shorter 
    #with the longest one among the nodes of the network
    def replace_chain(self): 
        #the set of nodes
        network = self.nodes 
        #introducing the variable that some point 
        #will become the chain of the node
        longest_chain = None
        #the length of the largest chain
        max_length = len(self.chain) 
        ##if finding a chain with hιgher length , variable will be updated
        ##Looping over the node in the network
        for node in network:
            response = requests.get(f'http://{node}/get_chain')
            if response.status_code == 200:
                length = response.json()['length']
                chain = response.json()['chain']
                ## if the chain in the node is valid and if the length 
                #of the other node is greater that the current
                if length > max_length and self.is_chain_valid(chain): 
                    ##if that is true---> updating the max_length variable
                    max_length = length
                    longest_chain = chain
        if longest_chain: 
            self.chain = longest_chain ##replacing the chain
            return True
        return False #as the chain was not replaced
# OrpheumCoin CODE

Consensus

# Μέρος 2ο

Δημιουργία

του Κρυπτονομίσματος

  • Δημιουργούμε μία εφαρμογή web βασισμένη στο Flask δημιουργώντας ένα αντικείμενο  της κλάσης Flask.
  • Στη συνέχεια δημιουργούμε μία μεταβλητή η οποία είναι η διεύθυνση του node και είναι απαραίτητη ώστε  οι miners να ανταμείβονται κατά την εξόρυξη ενός block.
  • Έπειτα δημιουργούμε το Orpheum Coin που είναι ουσιαστικά  ένα instance αντικείμενο της blockchain κλάσης μας.
# CREATING A WEB APP
app = Flask(__name__)
app.config['JSONIFY_PRETTYPRINT_REGULAR'] = False
#contains the address of the rewarder node
node_address = str(uuid4()).replace('-', '') 


#CREATING A BLOCKCHAIN
#instance object of our blockchain class
orpheum_blockchain = Blockchain() 

{Δημιουργία των Requests}

Mining

Προσθήκη συναλλαγών

Έλεγχος εγκυρότητας αλυσίδας

# Μέρος 2ο

Επικαιροποίηση αλυσίδας

Σύνδεση κόμβων

# OrpheumCoin CODE

Mining

#Mining a new block    
@app.route('/mine_block', methods = ['GET'])

def mine_block():
   #a. Solving PoW (in order to get that proof,we apply the 
   #PoW function. But in order to apply thePoW function 
   #we need the prev proof. We use the get_prev_block function)
    previous_block = orpheum_blockchain.get_previous_block()
    #b. getting the previous proof to call the Pow
    previous_proof = previous_block['proof']
    #c.getting the proof of the future new block by calling the PoW method
    proof = orpheum_blockchain.PoW(previous_proof) 
    #Adding the reward for the mining
    ##define sender,receiver & amount
    orpheum_blockchain.add_transaction(sender = node_address, 
                                       receiver = pu, amount = 20)
    previous_hash = orpheum_blockchain.hash(previous_block)
    #append the block to the blockchain
    block = orpheum_blockchain.create_block(proof, previous_hash)
    #getting the current hash
    block_hash = {'hash': orpheum_blockchain.hash(block)}
    #d. Displaying it in Postman
    response = {'message': 'CONGRATULATIONS!! MINING A BLOCK WAS SUCCESFULL!!', 
                'index': block['index'],
                'timestamp': block['timestamp'],
                'proof': block['proof'],
                'previous_hash': block['previous_hash'],
                'hash': block_hash['hash'],
                'transactions': block['transactions']}
    #adding current hash to the block
    block.update(block_hash)
    json.dumps(block)
    #we want to return an HTTP code for success (200), everything is OK
    return jsonify(response), 200
# OrpheumCoin CODE

Λήψη - έλεγχος εγκυρότητας αλυσίδας

#Create another request to get the whole blockchain
@app.route('/get_chain', methods = ['GET'])
#We are calling a new function in order to display the full chain 
def get_chain():
    response = {'chain': orpheum_blockchain.chain, 
                'length': len(orpheum_blockchain.chain)}
    return jsonify(response), 200
# Checking if the Blockchain is valid
@app.route('/is_valid', methods = ['GET'])
def is_valid():
    #calling the class function
    is_valid = orpheum_blockchain.is_chain_valid(orpheum_blockchain.chain)
    if is_valid:
        response = {'message': 'All good. The Blockchain is valid.'}
    else:
        response = {'message': 'Houston, we have a problem. The Blockchain is not valid.'}
    return jsonify(response), 200
# OrpheumCoin CODE

Προσθήκη - Υπογραφή και Επιβεβαίωση Συναλλαγής

## Adding a new transaction to the Blockchain
@app.route('/add_transaction', methods = ['GET']) 
def add_transaction():
    #Defining the receiver
    transaction_receiver = input("Enter receiver's public key:\n")
    #Defining the amount of Orpheum coins
    while True:
       try:
           transaction_amount = float(input("Enter the amount of Orpheum coins  you want to send:\n"))
           break
       except ValueError:
           print("ERROR!! Amount must be a number!!!")
    #adding transaction to the transaction list. Index is the actual message and will be used to string in bytes the message       
    index = orpheum_blockchain.add_transaction(sender = pu, receiver = transaction_receiver, 
                                               amount = transaction_amount)
    #from signature function
    message = bytes(str(index), 'utf-8') 
    
###Sign and verify the transaction
    if __name__ == '__main__':      
        sig = sign(message, private_key)
        print(sig)
        correct = verify(message, sig, public_key)
        print(correct)

        if correct:
            response = {'message': f'Success! Valid Signature! This transaction will be added to Block {index}'}
            return jsonify(response)
        else:
            response = {'message': 'Signature not Valid.Try Again'}    
            return jsonify(response),200

{Decentralize the Blockchain}

Είναι απαραίτητο για γίνουν τα εξής:

1. Να συνδεθούν όλοι οι κόμβοι σε αυτό το δίκτυο.

2. Η αλυσίδα να είναι επικαιροποιημένη σε όλους τους κόμβους και να αντικαθιστάται εάν υπάρχει μεγαλύτερη από αυτή του κόμβου.

# Μέρος 2ο
## 1.Connecting new nodes
@app.route('/connect_node', methods = ['POST'])
def connect_node():
    ## getting the request of posting a new node in the network
    json = request.get_json() 
    ##connecting a new node to all other nodes in the network
    nodes = json.get('nodes') 
    ##checking if the request is invalid
    if nodes is None:
        return "No node", 400
    ##looping over the addresses of the nodes and add them one by one.
    for node in nodes:
    ##use the add_node method in order to take the blockchain object
        orpheum_blockchain.add_node(node) 
    ## return the response    
    response = {'message': 'All  nodes are  connected. The Orpheum Blockchain 
                now contains the following nodes:', 
                'total_nodes': list(orpheum_blockchain.nodes)} 
    ##list here is a function of the object blockchain and the variable node
    #returning in json format
    return jsonify(response), 201 
# OrpheumCoin CODE

Σύνδεση κόμβων

## 2.Replacing the chain by the longest chain if needed
@app.route('/replace_chain', methods = ['GET'])
def replace_chain():
    ##A Boolean is needing to check if  the chain needs to be relaced. 
    #The replace_chain methon not only replace but returns true or false
    is_chain_replaced = orpheum_blockchain.replace_chain() 
    if is_chain_replaced: ##true
        response = {'message': 'The nodes had different chains so the 
                    chain was replaced by the longest one.',
                    'new_chain': orpheum_blockchain.chain}
    else:
        response = {'message': 'All good. The chain is the largest one.',
                    'actual_chain': orpheum_blockchain.chain}
    return jsonify(response), 200
# OrpheumCoin CODE

Αλλαγή αλυσίδας

Εκτέλεση εφαρμογής

#C. RUNNING THE APP
app.run(host = '0.0.0.0', port = 5000) 
# Μέρος 3ο

Παρουσίαση Εφαρμογής