import hashlib
import datetime

class Block:

	def __init__(self,index,proof_no,prev_hash,data,timestamp = None):
		self.index = index
		self.proof_no = proof_no
		self.prev_hash = prev_hash
		self.data = data
		self.timestamp = timestamp


	@property 
	def calculate_hash(self):
		block_of_string = ("{}{}{}{}".format(self.index,self.proof_no,self.prev_hash,self.data,self.timestamp))
		return(hashlib.sha256(block_of_string.encode())).hexdigest()

	def __repr__(self):
		return("{}-{}-{}-{}".format(self.index,self.proof_no,self.prev_hash,self.data,self.timestamp))


class BlockChain:
	#initiliazer
	def __init__(self):
		#empty lists to add blocks to that particular list
		self.chain = []
		self.current_data = []
		self.nodes = set()
		self.construct_genesis()


	#to construct the genesis block
	def construct_genesis(self):
		#initial block
		self.construct_block(proof_no = 0,prev_hash = 0)


	def construct_block(self,proof_no,prev_hash):
		block = Block(
			index = len(self.chain),
			proof_no = proof_no,
			prev_hash = prev_hash,
			data = self.current_data
			)
		self.current_data = []
		self.chain.append(block)
		return(block)

	@staticmethod
	def check_validity(block,prev_block):
		if(prev_block + 1 != block.index):
			return False
		elif(prev_block.calculate_hash != block.prev_hash):
			return False

		elif not Blockchain.verifying_proof(block.proof_no,prev_block.proof_no):
			return False

		elif(block.timestamp <= prev_block.timestamp):
			return False

		else:
			return True


	def new_data(self,sender,recipient,quantity):
		self.current_data.append({'sender':sender, 'recipient':recipient,'quantity':quantity})
		return True


	@staticmethod
	def proof_of_work(last_proof):
		"""
		this simple algorithm identifies a number of f' such taht hash(ff') contains 4 leading zeroes. f is the previous f'
		f' is the new proof
		"""

		proof_no = 0
		while (BlockChain.verifying_proof(proof_no,last_proof) is False):
			proof_no += 1
		return(proof_no)


	@staticmethod
	def verifying_proof(last_proof,proof):
		guess = f'{last_proof}{proof}'.encode()
		guess_hash = hashlib.sha256(guess).hexdigest()
		return(guess_hash[:4] == '0000')


	@property
	def latest_block(self):
		return(self.chain[-1]) #last block will be the latest block added to the blockchain



	def block_mining(self,details_miner):
		self.new_data(
			sender = "0",
			receiver = details_miner,
			quantity = 1
			)

		last_block = self.latest_block

		last_proof_no = last_block.proof_no
		proof_no = self.proof_of_work(last_proof)
		last_hash = last_block.calculate_hash
		block = self.construct_block(proof_no,last_hash)
		return(vars(block))


	def create_node(sef,address):
		self.nodes.add(address)
		return True


	@staticmethod
	def obtain_block_object(block_data):
		return(Block(
			block_data['index'],
			block_data['proof_no'],
			block_data['prev_hash'],
			block_data['data'],
			timestamp = block_data['timestamp']
			))


blockchain = BlockChain()

print("*******Mining the coins is about to start*******")
print(blockchain.chain)

last_block = blockchain.latest_block
last_proof_no = last_block.proof_no
proof_no = blockchain.proof_of_work(last_proof_no)




blockchain.new_data(
	sender = "0",
	recipient = "siddhanth",
	quantity = 1
)


last_hash = last_block.calculate_hash
block = blockchain.construct_block(proof_no,last_hash)


print("******Mining of coins has been successful******")
print(blockchain.chain)










