區塊鏈原理的實現(Python)

區塊結構

一個塊應該包含的信息:
塊的索引
時間戳
交易信息  

   一個數組,包含交易的發送者、接收者、交易的金額
工作量證明
上一個區塊的哈希值

 

 

實現區塊類結構

# {
#     "index":0,
#     "timestamp":"",
#     "transactions":[
#         {
#         "sender":"",
#         "recipient":"",
#         "amount":5,
#         }
#     ],
#     "proof":"",
#     "previous_hash":""
# }



class Blockchain:

    def __init__(self):
        self.chain = []     #數組中的元素都是一個個區塊
        self.current_transations = []  #保存當前的交易信息

    def new_block(self):    #新添加一個塊
        pass

    def new_transaction(self):    #新添加交易
        pass

    @staticmethod
    def hash(block):  #計算區塊的哈希值
        pass

    @property
    def last_block(self): #獲取區塊中的最後一個塊
        pass

 

添加交易

# {
#     "index":0,
#     "timestamp":"",
#     "transactions":[
#         {
#         "sender":"",
#         "recipient":"",
#         "amount":5,
#         }
#     ],
#     "proof":"",
#     "previous_hash":""
# }



class Blockchain:

    def __init__(self):
        self.chain = []     #數組中的元素都是一個個區塊
        self.current_transations = []  #保存當前的交易信息

    def new_block(self):    #新添加一個塊
        pass

    def new_transaction(self,sender,recipient,amount) -> int:    #新添加交易
        self.current_transations.append(
            {
                'sender':sender,
                'recipient':recipient,
                'amount':amount
            }
        )

        return self.last_block['index'] + 1

    @staticmethod
    def hash(block):  #計算區塊的哈希值
        pass

    @property
    def last_block(self): #獲取區塊中的最後一個塊
        pass

 

實現創建區塊

# {
#     "index":0,
#     "timestamp":"",
#     "transactions":[
#         {
#         "sender":"",
#         "recipient":"",
#         "amount":5,
#         }
#     ],
#     "proof":"",
#     "previous_hash":""
# }
import hashlib
import json
from time import time
from typing import Any, Dict, List, Optional
from urllib.parse import urlparse
from uuid import uuid4

import requests
from flask import Flask, jsonify, request


class Blockchain:

    def __init__(self):
        self.chain = []     #數組中的元素都是一個個區塊
        self.current_transations = []  #保存當前的交易信息
        #創世紀的區塊
        self.new_block(proof=100, previous_hash=1)

    def new_block(self,proof,previous_hash):    #新添加一個塊
        block = {
            'index':len(self.chain) + 1,
            'timestamp':time(),
            'transcations': self.current_transationsm,
            'proof':proof,
            'previous_hash':previous_hash or self.hash(self.self_block)
        }

        #交易已經打包成區塊了,把當前的交易清空
        self.current_transations = []
        self.chain.append(block)

        return block


    def new_transaction(self,sender,recipient,amount) -> int:    #新添加交易
        self.current_transations.append(
            {
                'sender':sender,
                'recipient':recipient,
                'amount':amount
            }
        )

        return self.last_block['index'] + 1

    @staticmethod
    def hash(block):  #計算區塊的哈希值
        #把block轉換成字節數組
        block_string = json.dumps(block,sorted_keys=True)
        hashlib.sha256(block_string).hexdigest()


    @property
    def last_block(self): #獲取區塊中的最後一個塊
        return self.chain(-1)

 

實現工作量證明

是否滿足以4個0開頭

import hashlib
import json
from time import time
from typing import Any, Dict, List, Optional
from urllib.parse import urlparse
from uuid import uuid4

import requests
from flask import Flask, jsonify, request


class Blockchain:

    def __init__(self):
        self.chain = []     #數組中的元素都是一個個區塊
        self.current_transations = []  #保存當前的交易信息
        #創世紀的區塊
        self.new_block(proof=100, previous_hash=1)

    def new_block(self,proof,previous_hash):    #新添加一個塊
        block = {
            'index':len(self.chain) + 1,
            'timestamp':time(),
            'transcations': self.current_transations,
            'proof':proof,
            'previous_hash':previous_hash or self.hash(self.self_block)
        }

        #交易已經打包成區塊了,把當前的交易清空
        self.current_transations = []
        self.chain.append(block)

        return block


    def new_transaction(self,sender,recipient,amount) -> int:    #新添加交易
        self.current_transations.append(
            {
                'sender':sender,
                'recipient':recipient,
                'amount':amount
            }
        )

        return self.last_block['index'] + 1

    @staticmethod
    def hash(block):  #計算區塊的哈希值
        #把block轉換成字節數組
        block_string = json.dumps(block,sorted_keys=True)
        hashlib.sha256(block_string).hexdigest()


    @property
    def last_block(self): #獲取區塊中的最後一個塊
        return self.chain(-1)


    def proof_of_work(self, last_proof:int) -> int:
        proof = 0
        while self.valid_proof(last_proof, proof) is False:
            proof += 1

        print(proof)
        return proof


    def valid_proof(self, last_proof: int, proof:int) -> bool:
        guess = f'{last_proof}{proof}'.encode()
        guess_hash = hashlib.sha256(guess).hexdigest()

        print(guess_hash)
        if guess_hash[0:4] == '0000':
            return True
        else:
            return False


if __name__ == '__main__':
    testPow = Blockchain()
    testPow.proof_of_work(100)

 

添加節點通信功能

通過Flask

import hashlib
import json
from time import time
from typing import Any, Dict, List, Optional
from urllib.parse import urlparse
from uuid import uuid4

import requests
from flask import Flask, jsonify, request


class Blockchain:

    def __init__(self):
        self.chain = []     #數組中的元素都是一個個區塊
        self.current_transations = []  #保存當前的交易信息
        #創世紀的區塊
        self.new_block(proof=100, previous_hash=1)

    def new_block(self,proof,previous_hash):    #新添加一個塊
        block = {
            'index':len(self.chain) + 1,
            'timestamp':time(),
            'transcations': self.current_transations,
            'proof':proof,
            'previous_hash':previous_hash or self.hash(self.self_block)
        }

        #交易已經打包成區塊了,把當前的交易清空
        self.current_transations = []
        self.chain.append(block)

        return block


    def new_transaction(self,sender,recipient,amount) -> int:    #新添加交易
        self.current_transations.append(
            {
                'sender':sender,
                'recipient':recipient,
                'amount':amount
            }
        )

        return self.last_block['index'] + 1

    @staticmethod
    def hash(block):  #計算區塊的哈希值
        #把block轉換成字節數組
        block_string = json.dumps(block,sorted_keys=True)
        hashlib.sha256(block_string).hexdigest()


    @property
    def last_block(self): #獲取區塊中的最後一個塊
        return self.chain(-1)


    def proof_of_work(self, last_proof:int) -> int:
        proof = 0
        while self.valid_proof(last_proof, proof) is False:
            proof += 1

        print(proof)
        return proof


    def valid_proof(self, last_proof: int, proof:int) -> bool:
        guess = f'{last_proof}{proof}'.encode()
        guess_hash = hashlib.sha256(guess).hexdigest()

        print(guess_hash)
        if guess_hash[0:4] == '0000':
            return True
        else:
            return False



app = Flask(__name__)

@app.route('/index',methods=['GET'])
def index():
    return 'Hello BlockChain'

@app.route('/transactions/new',methods=['POST'])
def new_transction():
    return "We'll add a new transctions"


@app.route('/mine',methods=['GET'])
def mine():
    return "We'll mine a new block"

@app.route('/chain', methods=['GET'])
def full_chain():
    return 'retutn full chain'


if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

 

交易接口實現

就是實現以下上面定義的幾個flask函數

import hashlib
import json
from time import time
from typing import Any, Dict, List, Optional
from urllib.parse import urlparse
from uuid import uuid4

import requests
from flask import Flask, jsonify, request


class Blockchain:

    def __init__(self):
        self.chain = []     #數組中的元素都是一個個區塊
        self.current_transations = []  #保存當前的交易信息
        #創世紀的區塊
        self.new_block(proof=100, previous_hash=1)

    def new_block(self,proof,previous_hash):    #新添加一個塊
        block = {
            'index':len(self.chain) + 1,
            'timestamp':time(),
            'transcations': self.current_transations,
            'proof':proof,
            'previous_hash':previous_hash or self.hash(self.self_block)
        }

        #交易已經打包成區塊了,把當前的交易清空
        self.current_transations = []
        self.chain.append(block)

        return block


    def new_transaction(self,sender,recipient,amount) -> int:    #新添加交易
        self.current_transations.append(
            {
                'sender':sender,
                'recipient':recipient,
                'amount':amount
            }
        )

        return self.last_block['index'] + 1

    @staticmethod
    def hash(block):  #計算區塊的哈希值
        #把block轉換成字節數組
        block_string = json.dumps(block,sorted_keys=True)
        hashlib.sha256(block_string).hexdigest()


    @property
    def last_block(self): #獲取區塊中的最後一個塊
        return self.chain[-1]


    def proof_of_work(self, last_proof:int) -> int:
        proof = 0
        while self.valid_proof(last_proof, proof) is False:
            proof += 1

        print(proof)
        return proof


    def valid_proof(self, last_proof: int, proof:int) -> bool:
        guess = f'{last_proof}{proof}'.encode()
        guess_hash = hashlib.sha256(guess).hexdigest()

        print(guess_hash)
        if guess_hash[0:4] == '0000':
            return True
        else:
            return False



app = Flask(__name__)
blockchain = Blockchain()

@app.route('/index',methods=['GET'])
def index():
    return 'Hello BlockChain'

@app.route('/transactions/new',methods=['POST'])
def new_transction():
    values = request.get_json()
    required = ['sender', 'recipient','amount']

    if values is None:
        return 'Missing values', 400
    if not all (k in values for k in required):
        return "Missing values", 400
    index = blockchain.new_transaction(values['sender'],
                                       values['recipient'],
                                       values['amount'])
    response = {"message":f'Transcation will be added to Block {index}'}

    return jsonify(response), 201


@app.route('/mine',methods=['GET'])
def mine():
    return "We'll mine a new block"

@app.route('/chain', methods=['GET'])
def full_chain():
    response = {
        'chain':blockchain.chain,
        'length': len(blockchain.chain)

    }
    return jsonify(response),200


if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

 

第一塊是創始塊,所以是第二塊

 

挖礦接口實現

import hashlib
import json
from time import time
from typing import Any, Dict, List, Optional
from urllib.parse import urlparse
from uuid import uuid4

import requests
from flask import Flask, jsonify, request


class Blockchain:

    def __init__(self):
        self.chain = []     #數組中的元素都是一個個區塊
        self.current_transations = []  #保存當前的交易信息
        #創世紀的區塊
        self.new_block(proof=100, previous_hash=1)

    def new_block(self,proof,previous_hash):    #新添加一個塊
        block = {
            'index':len(self.chain) + 1,
            'timestamp':time(),
            'transactions': self.current_transations,
            'proof':proof,
            'previous_hash':previous_hash or self.hash(self.chain[-1])
        }

        #交易已經打包成區塊了,把當前的交易清空
        self.current_transations = []
        self.chain.append(block)

        return block


    def new_transaction(self,sender,recipient,amount) -> int:    #新添加交易
        self.current_transations.append(
            {
                'sender':sender,
                'recipient':recipient,
                'amount':amount
            }
        )

        return self.last_block['index'] + 1

    @staticmethod
    def hash(block):  #計算區塊的哈希值
        #把block轉換成字節數組
        block_string = json.dumps(block, sort_keys=True).encode()
        return hashlib.sha256(block_string).hexdigest()


    @property
    def last_block(self): #獲取區塊中的最後一個塊
        return self.chain[-1]


    def proof_of_work(self, last_proof:int) -> int:
        proof = 0
        while self.valid_proof(last_proof, proof) is False:
            proof += 1

        # print(proof)
        return proof


    def valid_proof(self, last_proof: int, proof:int) -> bool:
        guess = f'{last_proof}{proof}'.encode()
        guess_hash = hashlib.sha256(guess).hexdigest()

        # print(guess_hash)
        if guess_hash[0:4] == '0000':
            return True
        else:
            return False



app = Flask(__name__)
blockchain = Blockchain()

node_identifier = str(uuid4()).replace('-', '')

@app.route('/index',methods=['GET'])
def index():
    return 'Hello BlockChain'

@app.route('/transactions/new',methods=['POST'])
def new_transction():
    values = request.get_json()
    required = ['sender', 'recipient','amount']

    if values is None:
        return 'Missing values', 400
    if not all (k in values for k in required):
        return "Missing values", 400
    index = blockchain.new_transaction(values['sender'],
                                       values['recipient'],
                                       values['amount'])
    response = {"message":f'Transcation will be added to Block {index}'}

    return jsonify(response), 201


@app.route('/mine',methods=['GET'])
def mine():
    #先算塊的工作量證明,然後給自己添加獎勵交易
    last_block = blockchain.last_block
    last_proof = last_block['proof']
    proof = blockchain.proof_of_work(last_proof)

    blockchain.new_transaction(sender="0",
                               recipient=node_identifier,
                               amount=1)
    
    block = blockchain.new_block(proof,None)
    
    response = {
        "message": "New Block Forged",
        "index": block['index'],
        "transactions": block['transactions'],
        "proof": block['proof'],
        "previous_hash": block['previous_hash']
    }

    return jsonify(response),200
    
@app.route('/chain', methods=['GET'])
def full_chain():
    response = {
        'chain':blockchain.chain,
        'length': len(blockchain.chain)

    }
    return jsonify(response),200


if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

成功挖了一個

返回所有區塊信息

 

這樣,一個單節點的區塊鏈就實現了

 

實現註冊節點

import hashlib
import json
from time import time
from typing import Any, Dict, List, Optional
from urllib.parse import urlparse
from uuid import uuid4

import requests
from flask import Flask, jsonify, request


class Blockchain:

    def __init__(self):
        self.chain = []     #數組中的元素都是一個個區塊
        self.current_transations = []  #保存當前的交易信息
        self.nodes = set()  #保存節點信息
        #創世紀的區塊
        self.new_block(proof=100, previous_hash=1)

    def register_node(self, address:str):
        #http://127.0.0.1:5001
        parsed_url = urlparse(address)
        self.nodes.add(parsed_url.netloc)



    def new_block(self,proof,previous_hash):    #新添加一個塊
        block = {
            'index':len(self.chain) + 1,
            'timestamp':time(),
            'transactions': self.current_transations,
            'proof':proof,
            'previous_hash':previous_hash or self.hash(self.chain[-1])
        }

        #交易已經打包成區塊了,把當前的交易清空
        self.current_transations = []
        self.chain.append(block)

        return block


    def new_transaction(self,sender,recipient,amount) -> int:    #新添加交易
        self.current_transations.append(
            {
                'sender':sender,
                'recipient':recipient,
                'amount':amount
            }
        )

        return self.last_block['index'] + 1

    @staticmethod
    def hash(block):  #計算區塊的哈希值
        #把block轉換成字節數組
        block_string = json.dumps(block, sort_keys=True).encode()
        return hashlib.sha256(block_string).hexdigest()


    @property
    def last_block(self): #獲取區塊中的最後一個塊
        return self.chain[-1]


    def proof_of_work(self, last_proof:int) -> int:
        proof = 0
        while self.valid_proof(last_proof, proof) is False:
            proof += 1

        # print(proof)
        return proof


    def valid_proof(self, last_proof: int, proof:int) -> bool:
        guess = f'{last_proof}{proof}'.encode()
        guess_hash = hashlib.sha256(guess).hexdigest()

        # print(guess_hash)
        if guess_hash[0:4] == '0000':
            return True
        else:
            return False



app = Flask(__name__)
blockchain = Blockchain()

node_identifier = str(uuid4()).replace('-', '')

@app.route('/index',methods=['GET'])
def index():
    return 'Hello BlockChain'

@app.route('/transactions/new',methods=['POST'])
def new_transction():
    values = request.get_json()
    required = ['sender', 'recipient','amount']

    if values is None:
        return 'Missing values', 400
    if not all (k in values for k in required):
        return "Missing values", 400
    index = blockchain.new_transaction(values['sender'],
                                       values['recipient'],
                                       values['amount'])
    response = {"message":f'Transcation will be added to Block {index}'}

    return jsonify(response), 201


@app.route('/mine',methods=['GET'])
def mine():
    #先算塊的工作量證明,然後給自己添加獎勵交易
    last_block = blockchain.last_block
    last_proof = last_block['proof']
    proof = blockchain.proof_of_work(last_proof)

    blockchain.new_transaction(sender="0",
                               recipient=node_identifier,
                               amount=1)
    
    block = blockchain.new_block(proof,None)
    
    response = {
        "message": "New Block Forged",
        "index": block['index'],
        "transactions": block['transactions'],
        "proof": block['proof'],
        "previous_hash": block['previous_hash']
    }

    return jsonify(response),200
    
@app.route('/chain', methods=['GET'])
def full_chain():
    response = {
        'chain':blockchain.chain,
        'length': len(blockchain.chain)

    }
    return jsonify(response),200


#{"nodes":["http://127.0.0.2:5000"]}
@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


if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

 

實現共識機制

import hashlib
import json
from time import time
from typing import Any, Dict, List, Optional
from urllib.parse import urlparse
from uuid import uuid4

import requests
from flask import Flask, jsonify, request
from argparse import ArgumentParser


class Blockchain:

    def __init__(self):
        self.chain = []     #數組中的元素都是一個個區塊
        self.current_transations = []  #保存當前的交易信息
        self.nodes = set()  #保存節點信息
        #創世紀的區塊
        self.new_block(proof=100, previous_hash=1)

    def register_node(self, address:str):
        #http://127.0.0.1:5001
        parsed_url = urlparse(address)
        self.nodes.add(parsed_url.netloc)

    def valid_chain(self, chain) -> bool:
        #判斷依據就是看它的每個哈希值是否是上一個塊的哈希
        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

            if not self.valid_proof(last_block['proof'], block['proof']): #如果工作量證明不滿足
                return False

            last_block = block
            current_index += 1

        return True


    def resolve_conflicts(self) -> bool:

        neighbours = self.nodes  #相鄰的節點

        max_length = len(self.chain)
        new_chain = None

        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


    def new_block(self,proof,previous_hash):    #新添加一個塊
        block = {
            'index':len(self.chain) + 1,
            'timestamp':time(),
            'transactions': self.current_transations,
            'proof':proof,
            'previous_hash':previous_hash or self.hash(self.chain[-1])
        }

        #交易已經打包成區塊了,把當前的交易清空
        self.current_transations = []
        self.chain.append(block)

        return block


    def new_transaction(self,sender,recipient,amount) -> int:    #新添加交易
        self.current_transations.append(
            {
                'sender':sender,
                'recipient':recipient,
                'amount':amount
            }
        )

        return self.last_block['index'] + 1

    @staticmethod
    def hash(block):  #計算區塊的哈希值
        #把block轉換成字節數組
        block_string = json.dumps(block, sort_keys=True).encode()
        return hashlib.sha256(block_string).hexdigest()


    @property
    def last_block(self): #獲取區塊中的最後一個塊
        return self.chain[-1]


    def proof_of_work(self, last_proof:int) -> int:
        proof = 0
        while self.valid_proof(last_proof, proof) is False:
            proof += 1

        # print(proof)
        return proof


    def valid_proof(self, last_proof: int, proof:int) -> bool:
        guess = f'{last_proof}{proof}'.encode()
        guess_hash = hashlib.sha256(guess).hexdigest()

        # print(guess_hash)
        if guess_hash[0:4] == '0000':
            return True
        else:
            return False



app = Flask(__name__)
blockchain = Blockchain()

node_identifier = str(uuid4()).replace('-', '')

@app.route('/index',methods=['GET'])
def index():
    return 'Hello BlockChain'

@app.route('/transactions/new',methods=['POST'])
def new_transction():
    values = request.get_json()
    required = ['sender', 'recipient','amount']

    if values is None:
        return 'Missing values', 400
    if not all (k in values for k in required):
        return "Missing values", 400
    index = blockchain.new_transaction(values['sender'],
                                       values['recipient'],
                                       values['amount'])
    response = {"message":f'Transcation will be added to Block {index}'}

    return jsonify(response), 201


@app.route('/mine',methods=['GET'])
def mine():
    #先算塊的工作量證明,然後給自己添加獎勵交易
    last_block = blockchain.last_block
    last_proof = last_block['proof']
    proof = blockchain.proof_of_work(last_proof)

    blockchain.new_transaction(sender="0",
                               recipient=node_identifier,
                               amount=1)
    
    block = blockchain.new_block(proof,None)
    
    response = {
        "message": "New Block Forged",
        "index": block['index'],
        "transactions": block['transactions'],
        "proof": block['proof'],
        "previous_hash": block['previous_hash']
    }

    return jsonify(response),200
    
@app.route('/chain', methods=['GET'])
def full_chain():
    response = {
        'chain':blockchain.chain,
        'length': len(blockchain.chain)

    }
    return jsonify(response),200


#{"nodes":["http://127.0.0.2:5000"]}
@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

@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__':
    parser = ArgumentParser()
    parser.add_argument('-p','--port',default=5051, type=int,help='port to listen to')
    args = parser.parse_args()
    port = args.port
    
    app.run(host='0.0.0.0', port=port)

 

分別在5000和5001起兩個終端

先看一下5000的鏈條

長度是1

然後我們給5000節點添加一個交易

此時鏈條長度還是1,因爲我們沒有挖礦

請求挖礦

此時看一下鏈條長度

長度是2

 

此時看一下5001節點的鏈條長度

長度是1

 

這個時候,就發生了節點5000和節點5001的長度是不一樣的

按照共識機制,應該是採用5000節點的長度

然後現在我們把節點5000節點註冊到5001上去,將5001節點註冊到5000上

顯示節點已添加

顯示節點已添加

此時查看5001節點解決衝突的方法

鏈條已經被替換成長的

此時查看5001的鏈條長度

 

至此,一個區塊鏈的雛形就實現了

總的

blockchain.py

import hashlib
import json
from time import time
from typing import Any, Dict, List, Optional
from urllib.parse import urlparse
from uuid import uuid4

import requests
from flask import Flask, jsonify, request
from argparse import ArgumentParser


class Blockchain:

    def __init__(self):
        self.chain = []     #數組中的元素都是一個個區塊
        self.current_transations = []  #保存當前的交易信息
        self.nodes = set()  #保存節點信息
        #創世紀的區塊
        self.new_block(proof=100, previous_hash=1)

    def register_node(self, address:str):
        #http://127.0.0.1:5001
        parsed_url = urlparse(address)
        self.nodes.add(parsed_url.netloc)

    def valid_chain(self, chain) -> bool:
        #判斷依據就是看它的每個哈希值是否是上一個塊的哈希
        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

            if not self.valid_proof(last_block['proof'], block['proof']): #如果工作量證明不滿足
                return False

            last_block = block
            current_index += 1

        return True


    def resolve_conflicts(self) -> bool:

        neighbours = self.nodes  #相鄰的節點

        max_length = len(self.chain)
        new_chain = None

        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


    def new_block(self,proof,previous_hash):    #新添加一個塊
        block = {
            'index':len(self.chain) + 1,
            'timestamp':time(),
            'transactions': self.current_transations,
            'proof':proof,
            'previous_hash':previous_hash or self.hash(self.chain[-1])
        }

        #交易已經打包成區塊了,把當前的交易清空
        self.current_transations = []
        self.chain.append(block)

        return block


    def new_transaction(self,sender,recipient,amount) -> int:    #新添加交易
        self.current_transations.append(
            {
                'sender':sender,
                'recipient':recipient,
                'amount':amount
            }
        )

        return self.last_block['index'] + 1

    @staticmethod
    def hash(block):  #計算區塊的哈希值
        #把block轉換成字節數組
        block_string = json.dumps(block, sort_keys=True).encode()
        return hashlib.sha256(block_string).hexdigest()


    @property
    def last_block(self): #獲取區塊中的最後一個塊
        return self.chain[-1]


    def proof_of_work(self, last_proof:int) -> int:
        proof = 0
        while self.valid_proof(last_proof, proof) is False:
            proof += 1

        # print(proof)
        return proof


    def valid_proof(self, last_proof: int, proof:int) -> bool:
        guess = f'{last_proof}{proof}'.encode()
        guess_hash = hashlib.sha256(guess).hexdigest()

        # print(guess_hash)
        if guess_hash[0:4] == '0000':
            return True
        else:
            return False



app = Flask(__name__)
blockchain = Blockchain()

node_identifier = str(uuid4()).replace('-', '')

@app.route('/index',methods=['GET'])
def index():
    return 'Hello BlockChain'

@app.route('/transactions/new',methods=['POST'])
def new_transction():
    values = request.get_json()
    required = ['sender', 'recipient','amount']

    if values is None:
        return 'Missing values', 400
    if not all (k in values for k in required):
        return "Missing values", 400
    index = blockchain.new_transaction(values['sender'],
                                       values['recipient'],
                                       values['amount'])
    response = {"message":f'Transcation will be added to Block {index}'}

    return jsonify(response), 201


@app.route('/mine',methods=['GET'])
def mine():
    #先算塊的工作量證明,然後給自己添加獎勵交易
    last_block = blockchain.last_block
    last_proof = last_block['proof']
    proof = blockchain.proof_of_work(last_proof)

    blockchain.new_transaction(sender="0",
                               recipient=node_identifier,
                               amount=1)
    
    block = blockchain.new_block(proof,None)
    
    response = {
        "message": "New Block Forged",
        "index": block['index'],
        "transactions": block['transactions'],
        "proof": block['proof'],
        "previous_hash": block['previous_hash']
    }

    return jsonify(response),200
    
@app.route('/chain', methods=['GET'])
def full_chain():
    response = {
        'chain':blockchain.chain,
        'length': len(blockchain.chain)

    }
    return jsonify(response),200


#{"nodes":["http://127.0.0.2:5000"]}
@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

@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__':
    parser = ArgumentParser()
    parser.add_argument('-p','--port',default=5000, type=int,help='port to listen to')
    args = parser.parse_args()
    port = args.port
    
    app.run(host='0.0.0.0', port=port)

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章