blockshell/blockchain/chain.py

194 lines
6.2 KiB
Python

# -*- coding: utf-8 -*-
# ===================================================
# ==================== META DATA ===================
# ==================================================
__author__ = "Daxeel Soni"
__url__ = "https://daxeel.github.io"
__email__ = "daxeelsoni44@gmail.com"
__license__ = "MIT"
__version__ = "0.1"
__maintainer__ = "Daxeel Soni"
# ==================================================
# ================= IMPORT MODULES =================
# ==================================================
import os
import hashlib
import datetime
import json
from colorama import Fore, Back, Style
import time
import sys
import base64
from numpy import random
from PIL import Image
# ==================================================
# =================== BLOCK CLASS ==================
# ==================================================
class Block:
"""
Create a new block in chain with metadata
"""
def __init__(self):
self.index = -1
self.previousHash = ""
self.uid_epita = -1
self.email_epita = ""
self.firstname = ""
self.lastname = ""
self.image = ""
self.timestamp = str(datetime.datetime.now())
self.nonce = 0
self.hash = self.calculateHash()
@classmethod
def fromSave(cls, data):
block = cls()
for key, value in data.items():
setattr(block, key, value)
return block
@classmethod
def fromValues(cls, uid_epita, email_epita, firstname, lastname, index = 0):
block = cls()
block.index = index
block.uid_epita = uid_epita
block.email_epita = email_epita
block.firstname = firstname
block.lastname = lastname
block.createRandomImage()
return block
def calculateHash(self):
"""
Method to calculate hash from metadata
"""
hashData = str(self.index) + str(self.previousHash) + str(self.uid_epita) + str(self.email_epita) + str(self.firstname) + str(self.lastname) + str(self.image) + str(self.timestamp) + str(self.nonce)
return hashlib.sha256(hashData.encode('utf-8')).hexdigest()
def mineBlock(self, difficulty):
"""
Method for Proof of Work
"""
print(Back.RED + "\n[Status] Mining block (" + str(self.index) + ") with PoW ...")
startTime = time.time()
self.hash = ""
while self.hash[:difficulty] != "0"*difficulty:
self.nonce += 1
self.hash = self.calculateHash()
endTime = time.time()
print(Back.BLUE + "[ Info ] Time Elapsed : " + str(endTime - startTime) + " seconds.")
print(Back.BLUE + "[ Info ] Mined Hash : " + self.hash)
print(Style.RESET_ALL)
def createRandomImage(self):
"""
Method to create random image in base64
"""
imgarray = random.rand(30, 30, 3) * 255
image = Image.fromarray(imgarray.astype('uint8')).convert('RGB')
image.save('random.png')
with open("random.png", "rb") as imageFile:
self.image = str(base64.b64encode(imageFile.read()))
if os.path.exists("random.png"):
os.remove("random.png")
# ==================================================
# ================ BLOCKCHAIN CLASS ================
# ==================================================
class Blockchain:
"""
Initialize blockchain
"""
def __init__(self):
self.chain = [self.createGenesisBlock()]
self.difficulty = 3
def createGenesisBlock(self):
"""
Method create genesis block
"""
return Block.fromValues(-1, "init@epita.fr", "Genesis", "Block")
def addBlock(self, newBlock):
"""
Method to add new block from Block class
"""
newBlock.index = len(self.chain)
newBlock.previousHash = self.chain[-1].hash
newBlock.mineBlock(self.difficulty)
self.chain.append(newBlock)
self.writeBlocks()
def writeBlocks(self):
"""
Method to write new mined block to blockchain
"""
with open("chain.txt", "w") as dataFile:
chainData = []
for eachBlock in self.chain:
chainData.append(eachBlock.__dict__)
dataFile.write(json.dumps(chainData, indent=4))
def loadBlocks(self, filename):
"""
Method to load existing blockchain from file
"""
try:
with open(filename, "r") as dataFile:
chainData = json.loads(dataFile.read())
self.chain = []
for eachBlock in chainData:
self.chain.append(Block.fromSave(eachBlock))
except Exception as e:
print(Back.RED + "[Error] No existing blockchain found.")
print(Back.RED + "[Error] Creating new blockchain.")
print(Style.RESET_ALL)
self.writeBlocks()
def modifyBlock(self, hash, uid_epita, email_epita, firstname, lastname):
"""
Method to modify existing block
"""
i = 0
for i in range(len(self.chain)):
eachBlock = self.chain[i]
if eachBlock.hash == hash:
eachBlock.uid_epita = uid_epita
eachBlock.email_epita = email_epita
eachBlock.firstname = firstname
eachBlock.lastname = lastname
eachBlock.createRandomImage()
eachBlock.mineBlock(self.difficulty)
break
if i == len(self.chain) - 1:
return False
for i in range(i+1, len(self.chain)):
eachBlock = self.chain[i]
eachBlock.index = i
eachBlock.previousHash = self.chain[i-1].hash
eachBlock.mineBlock(self.difficulty)
self.writeBlocks()
return True
def isChainValid(self):
"""
Method to verify integrity of blockchain
"""
for i in range(1, len(self.chain)):
eachBlock = self.chain[i]
if eachBlock.hash != eachBlock.calculateHash():
return i
if eachBlock.previousHash != self.chain[i-1].hash:
return i
return 0