import hashlib

import json

from time import time



class Blockchain(object):

   def __init__(self):

       self.current_transactions = []

       self.chain = []


       # 创建创世区块

       self.new_block(proof=100, previous_hash=1)


   def new_block(self, proof, previous_hash=None):

       """

       创建一个新的区块到区块链中

       :param proof: <int> 由工作证明算法生成的证明

       :param previous_hash: (Optional) <str> 前一个区块的 hash 值

       :return: <dict> 新区块

       """


       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


   def new_transaction(self, sender, recipient, amount):

       """

       创建一笔新的交易到下一个被挖掘的区块中

       :param sender: <str> 发送人的地址

       :param recipient: <str> 接收人的地址

       :param amount: <int> 金额

       :return: <int> 持有本次交易的区块索引

       """

       self.current_transactions.append({

           'sender': sender,

           'recipient': recipient,

           'amount': amount,

       })


       return self.last_block['index'] + 1


   @property

   def last_block(self):

       return self.chain[-1]


   @staticmethod

   def hash(block):

       """

       给一个区块生成 SHA-256 值

       :param block: <dict> Block

       :return: <str>

       """


       # 我们必须确保这个字典(区块)是经过排序的,否则我们将会得到不一致的散列

       block_string = json.dumps(block, sort_keys=True).encode()

       return hashlib.sha256(block_string).hexdigest()


   def proof_of_work(self, last_proof):

       """

       Simple Proof of Work Algorithm:

        - Find a number p' such that hash(pp') contains leading 4 zeroes, where p is the previous p'

        - p is the previous proof, and p' is the new proof

       :param last_proof: <int>

       :return: <int>

       """


       proof = 0

       while self.valid_proof(last_proof, proof) is False:

           proof += 1


       return proof


   @staticmethod

   def valid_proof(last_proof, proof):

       """

       Validates the Proof: Does hash(last_proof, proof) contain 4 leading zeroes?

       :param last_proof: <int> Previous Proof

       :param proof: <int> Current Proof

       :return: <bool> True if correct, False if not.

       """


       guess = '{last_proof}{proof}'.format(last_proof=last_proof, proof=proof).encode()

       guess_hash = hashlib.sha256(guess).hexdigest()

       return guess_hash[:4] == "0000"



   def chainIsValid(self):

       """

       校验hash proof

       """


       for index in range(1, len(self.chain)):

           currentBlock = self.chain[index]

           previousBlock = self.chain[index-1]

           previous_hash = currentBlock['previous_hash']

           current_hash = self.hash(previousBlock)


           previous_proof = previousBlock['proof']

           current_proof = currentBlock['proof']


           if (previous_hash != current_hash) and valid_proof(previous_proof, current_proof):

               return False

           else:

               return True



if __name__=='__main__':



   t = Blockchain()

   t.new_transaction('a','b',1000)

   previous_hash = t.hash(t.last_block)

   last_proof = t.last_block['proof']

   proof = t.proof_of_work(last_proof)

   data = t.new_block(proof, previous_hash)

   print(data)

   print(t.chain)




   t.new_transaction('a','c',80)

   t.new_transaction('a','d',980)

   previous_hash = t.hash(t.last_block)

   last_proof = t.last_block['proof']

   proof = t.proof_of_work(last_proof)

   data = t.new_block(proof, previous_hash)

   print(data)

   print(t.chain)



   t.new_transaction('a','e',280)

   t.new_transaction('b','e',180)

   previous_hash = t.hash(t.last_block)

   last_proof = t.last_block['proof']

   proof = t.proof_of_work(last_proof)

   data = t.new_block(proof, previous_hash)

   print(data)

   print(t.chain)


   t.new_transaction('d','e',810)

   t.new_transaction('a','e',280)

   previous_hash = t.hash(t.last_block)

   last_proof = t.last_block['proof']

   proof = t.proof_of_work(last_proof)

   data = t.new_block(proof, previous_hash)

   print(data)

   print(t.chain)



   t.new_transaction('b','a',2180)

   t.new_transaction('c','d',1280)

   previous_hash = t.hash(t.last_block)

   last_proof = t.last_block['proof']

   proof = t.proof_of_work(last_proof)

   data = t.new_block(proof, previous_hash)

   print(data)

   print(t.chain)



   print(t.chainIsValid())

   for info in t.chain:

       print(info['proof'])