區(qū)塊鏈節(jié)點(diǎn)信息需要同步,本節(jié)主要講一下選取最長的鏈做為基準(zhǔn)鏈。
NiuCoinBlockChainOne.py
# -*- coding: utf-8 -*-
from typing import Any,Dict,List,Optional
import time
import hashlib
from uuid import uuid4
import json
from flask import Flask
from flask import jsonify
from urllib.parse import urlparse
from flask import request
import requests
class NiuCoinBlockChainOne:
# 構(gòu)造器
def __init__(self):
self.current_transcations=[]#緩存交易列表,在沒有新區(qū)塊前放在這里,產(chǎn)生新區(qū)塊后,會(huì)被加入到新區(qū)塊
self.chain=[]#區(qū)塊鏈管理多個(gè)區(qū)塊
self.nodes=set()#保存網(wǎng)絡(luò)中的其它節(jié)點(diǎn)
self.new_block(pre_hash="1",proof=100)#創(chuàng)世區(qū)塊
#新增一個(gè)新區(qū)塊
def new_block(self,pre_hash:Optional[str],proof:int)->Dict[str,Any]:
block = {
"index":len(self.chain)+1, #索引
"timestamp":time.time(),
"transcations":self.current_transcations,#將臨時(shí)緩存的交易放在區(qū)塊中
"proof":proof,#工作量要記下來,校驗(yàn)區(qū)塊時(shí)需要這個(gè)證明
"pre_hash":pre_hash or self.chain[-1]["hash"]
}
block["hash"] = self.hash(block)
self.current_transcations=[]#交易被打包成區(qū)塊后,要清空
self.chain.append(block)
#新增一個(gè)新的交易
def new_transcations(self,sender:str,recer:str,amount:int)->int:
self.current_transcations.append({
"sender":sender,
"recer":recer,
"amount":amount,
})
return self.last_block()["index"]+1
# 獲得最后一個(gè)區(qū)塊
def last_block(self)->Dict[str,Any]:
return self.chain[-1]
# 整個(gè)區(qū)塊hash
@staticmethod
def hash(block:Dict[str,Any])->str:
blockstring = json.dumps(block,sort_keys=True).encode()
return hashlib.sha256(blockstring).hexdigest()
# 不斷修改值,進(jìn)行hash碰撞,起到算出結(jié)果為止(即拿到證明)
def proof_of_work(self,last_proof:int)->int:
proof = 0
while self.do_proof(last_proof,proof) is False:
proof+=1
return proof
# 進(jìn)行hash碰撞
@staticmethod
def do_proof(last_proof:int,proof:int)->bool:
guess = f'{last_proof*proof}'.encode()
guess_hash = hashlib.sha256(guess).hexdigest()
return guess_hash[:4]=="0000"#難度可調(diào)
# 校驗(yàn)整個(gè)鏈?zhǔn)欠裾_
def valid_chain(self,chain:List[Dict[str,Any]]):
#用當(dāng)前區(qū)塊與前一個(gè)區(qū)塊進(jìn)行校驗(yàn),循環(huán)至最后一個(gè)
pre_block = chain[0]
current_index = 1
while current_index<len(chain):
block = chain[current_index]
if block["pre_hash"]!=self.hash(pre_block): #校驗(yàn)是否被篡改,hash要能對上
return False
if not self.valid_proof(pre_block["proof"],block["proof"]):#校驗(yàn)工作量正確
return False
pre_block = block
current_index+=1
return True
# 節(jié)點(diǎn)注冊,追加節(jié)點(diǎn)
def register_node(self,addr:str)->None:
now_url=urlparse(addr)#將addr轉(zhuǎn)成對象,能取出schame或port等
self.nodes.add(now_url.netloc)#netloc='www.cwi.nl:80'
#節(jié)點(diǎn)同步:查找所有的節(jié)點(diǎn),如果附近的節(jié)點(diǎn)比自己多,將鄰居節(jié)點(diǎn)的鏈賦值給自己節(jié)點(diǎn)
def resolve_conflicts(self)->bool:
neighbours=self.nodes
new_chain=None
max_length=len(self.chain)
for node in neighbours:#從鄰居中取最長的鏈,賦值給new_chain
response = requests.get(f"http://{node}/chain") #node的域名與端口都不一樣
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
niucoin = NiuCoinBlockChainOne()
node_id = str(uuid4()).replace("-","")
print("當(dāng)前節(jié)點(diǎn)錢包地址:",node_id)
app = Flask(__name__)
@app.route("/")
def index_page():
return "歡迎呵呵"
@app.route("/chain")
def chain():#輸出區(qū)塊鏈
response={
"chain":niucoin.chain,
"length":len(niucoin.chain),
}
return jsonify(response),200
@app.route("/mine")#挖礦,系統(tǒng)獎(jiǎng)勵(lì)比特幣
def index_mine():
last_block = niucoin.last_block()
last_proof = last_block["proof"]
proof = niucoin.proof_of_work(last_proof) #挖礦,挖到礦才往下走
niucoin.new_transcations("0",node_id,200) #獲得獎(jiǎng)勵(lì)
niucoin.new_block(None,proof)
response={
"message":"新的區(qū)塊產(chǎn)生",
"index":niucoin.last_block()["index"],
"transcations":niucoin.last_block()["transcations"],
"proof":niucoin.last_block()["proof"],
"pre_hash":niucoin.last_block()["pre_hash"]
}
return jsonify(response),200
@app.route("/new_transcations",methods=["POST"])#交易
def new_transcations():
values = request.get_json()
required = ["payer","recer","amount"]
if not all(key in values for key in required):
return "數(shù)據(jù)不完整",400
index = niucoin.new_transcations(values["payer"],values["recer"],values["amount"])
response = {
"message":f"交易加入到區(qū)塊{index}"
}
return jsonify(response),200
@app.route("/new_node",methods=["POST"])#新增一個(gè)節(jié)點(diǎn)
def new_node():
values = request.get_json()
nodes = values.get("nodes")#獲得所有節(jié)點(diǎn)
if nodes is None:
return "數(shù)據(jù)有問題",400
for node in nodes:
niucoin.register_node(node) #加入節(jié)點(diǎn)
response = {
"message":f"節(jié)點(diǎn)已經(jīng)追加",
"node":list(niucoin.nodes)
}
return jsonify(response),200
@app.route("/node_synchronize")#節(jié)點(diǎn)信息同步
def node_refresh():
replaced = niucoin.resolve_conflicts() #最長的鏈
if replaced:
response={
"message":"區(qū)塊鏈查新為最長區(qū)塊鏈",
"new_chain":niucoin.chain
}
else:
response = {
"message": "區(qū)塊鏈已經(jīng)是最長的,無需替換",
"new_chain": niucoin.chain
}
return jsonify(response), 200
if __name__ == "__main__":
app.run("127.0.0.1",port=65531)
NiuCoinBlockChainTwo.py
# -*- coding: utf-8 -*-
from typing import Any,Dict,List,Optional
import time
import hashlib
from uuid import uuid4
import json
from flask import Flask
from flask import jsonify
from urllib.parse import urlparse
from flask import request
import requests
class NiuCoinBlockChainTwo:
#構(gòu)造器
def __init__(self):
self.current_transcations=[]#緩存交易列表,在沒有新區(qū)塊前放在這里,產(chǎn)生新區(qū)塊后,會(huì)被加入到新區(qū)塊
self.chain=[]#區(qū)塊鏈管理多個(gè)區(qū)塊
self.nodes=set()#保存網(wǎng)絡(luò)中的其它節(jié)點(diǎn)
self.new_block(pre_hash="1",proof=100)#創(chuàng)世區(qū)塊
#新增一個(gè)新區(qū)塊
def new_block(self,pre_hash:Optional[str],proof:int)->Dict[str,Any]:
block = {
"index":len(self.chain)+1, #索引
"timestamp":time.time(),
"transcations":self.current_transcations,#將臨時(shí)緩存的交易放在區(qū)塊中
"proof":proof,#工作量要記下來,校驗(yàn)區(qū)塊時(shí)需要這個(gè)證明
"pre_hash":pre_hash or self.chain[-1]["hash"]
}
block["hash"] = self.hash(block)
self.current_transcations=[]#交易被打包成區(qū)塊后,要清空
self.chain.append(block)
#新增一個(gè)新的交易
def new_transcations(self,sender:str,recer:str,amount:int)->int:
self.current_transcations.append({
"sender":sender,
"recer":recer,
"amount":amount,
})
return self.last_block()["index"]+1
# 獲得最后一個(gè)區(qū)塊
def last_block(self)->Dict[str,Any]:
return self.chain[-1]
# 整個(gè)區(qū)塊hash
@staticmethod
def hash(block:Dict[str,Any])->str:
blockstring = json.dumps(block,sort_keys=True).encode()
return hashlib.sha256(blockstring).hexdigest()
# 不斷修改值,進(jìn)行hash碰撞,起到算出結(jié)果為止(即拿到證明)
def proof_of_work(self,last_proof:int)->int:
proof = 0
while self.do_proof(last_proof,proof) is False:
proof+=1
return proof
# 進(jìn)行hash碰撞
@staticmethod
def do_proof(last_proof:int,proof:int)->bool:
guess=f'{last_proof*proof}'.encode()
guess_hash = hashlib.sha256(guess).hexdigest()
return guess_hash[:4]=="0000"#難度可調(diào)
# 校驗(yàn)整個(gè)鏈?zhǔn)欠裾_
def valid_chain(self,chain:List[Dict[str,Any]]):
#用當(dāng)前區(qū)塊與前一個(gè)區(qū)塊進(jìn)行校驗(yàn),循環(huán)至最后一個(gè)
pre_block = chain[0]
current_index = 1
while current_index<len(chain):
block = chain[current_index]
if block["pre_hash"]!=self.hash(pre_block): #校驗(yàn)是否被篡改,hash要能對上
return False
if not self.valid_proof(pre_block["proof"],block["proof"]):#校驗(yàn)工作量正確
return False
pre_block = block
current_index+=1
return True
# 節(jié)點(diǎn)注冊,追加節(jié)點(diǎn)
def register_node(self,addr:str)->None:
now_url=urlparse(addr)#將addr轉(zhuǎn)成對象,能取出schame或port等
self.nodes.add(now_url.path)#netloc='www.cwi.nl:80'
#節(jié)點(diǎn)同步:查找所有的節(jié)點(diǎn),如果附近的節(jié)點(diǎn)比自己多,將鄰居節(jié)點(diǎn)的鏈賦值給自己節(jié)點(diǎn)
def resolve_conflicts(self)->bool:
neighbours=self.nodes
new_chain=None
max_length=len(self.chain)
for node in neighbours:#從鄰居中取最長的鏈,賦值給new_chain
response = requests.get(f"http://{node}/chain") #node的域名與端口都不一樣
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
niucoin = NiuCoinBlockChainTwo()
node_id = str(uuid4()).replace("-","")
print("當(dāng)前節(jié)點(diǎn)錢包地址:",node_id)
app = Flask(__name__)
@app.route("/")
def index_page():
return "歡迎呵呵"
@app.route("/chain")
def chain():#輸出區(qū)塊鏈
response={
"chain":niucoin.chain,
"length":len(niucoin.chain),
}
return jsonify(response),200
@app.route("/mine")#挖礦,系統(tǒng)獎(jiǎng)勵(lì)比特幣
def index_mine():
last_block = niucoin.last_block()
last_proof = last_block["proof"]
proof = niucoin.proof_of_work(last_proof) #挖礦,挖到礦才往下走
niucoin.new_transcations("0",node_id,200) #獲得獎(jiǎng)勵(lì)
niucoin.new_block(None,proof)
response={
"message":"新的區(qū)塊產(chǎn)生",
"index":niucoin.last_block()["index"],
"transcations":niucoin.last_block()["transcations"],
"proof":niucoin.last_block()["proof"],
"pre_hash":niucoin.last_block()["pre_hash"]
}
return jsonify(response),200
@app.route("/new_transcations",methods=["POST"])#交易
def new_transcations():
values = request.get_json()
required = ["payer","recer","amount"]
if not all(key in values for key in required):
return "數(shù)據(jù)不完整",400
index = niucoin.new_transcations(values["payer"],values["recer"],values["amount"])
response = {
"message":f"交易加入到區(qū)塊{index}"
}
return jsonify(response),200
@app.route("/new_node",methods=["POST"])
def new_node():
values = request.get_json()
nodes = values.get("nodes")#獲得所有節(jié)點(diǎn)
if nodes is None:
return "數(shù)據(jù)有問題",400
for node in nodes:
niucoin.register_node(node) #加入節(jié)點(diǎn)
response = {
"message":f"節(jié)點(diǎn)已經(jīng)追加",
"node":list(niucoin.nodes)
}
return jsonify(response),200
@app.route("/node_refresh")
def node_refresh():
replaced = niucoin.resolve_conflicts() #最長的鏈
if replaced:
response={
"message":"區(qū)塊鏈查新為最長區(qū)塊鏈",
"new_chain":niucoin.chain
}
else:
response = {
"message": "區(qū)塊鏈已經(jīng)是最長的,無需替換",
"new_chain": niucoin.chain
}
return jsonify(response), 200
if __name__ == "__main__":
app.run("127.0.0.1",port=65532)
NiuCoinBlockChainThree.py
# -*- coding: utf-8 -*-
from typing import Any,Dict,List,Optional
import time
import hashlib
from uuid import uuid4
import json
from flask import Flask
from flask import jsonify
from urllib.parse import urlparse
from flask import request
import requests
class NiuCoinBlockChainThree:
#構(gòu)造器
def __init__(self):
self.current_transcations=[]#緩存交易列表,在沒有新區(qū)塊前放在這里,產(chǎn)生新區(qū)塊后,會(huì)被加入到新區(qū)塊
self.chain=[]#區(qū)塊鏈管理多個(gè)區(qū)塊
self.nodes=set()#保存網(wǎng)絡(luò)中的其它節(jié)點(diǎn)
self.new_block(pre_hash="1",proof=100)#創(chuàng)世區(qū)塊
#新增一個(gè)新區(qū)塊
def new_block(self,pre_hash:Optional[str],proof:int)->Dict[str,Any]:
block = {
"index":len(self.chain)+1, #索引
"timestamp":time.time(),
"transcations":self.current_transcations,#將臨時(shí)緩存的交易放在區(qū)塊中
"proof":proof,#工作量要記下來,校驗(yàn)區(qū)塊時(shí)需要這個(gè)證明
"pre_hash":pre_hash or self.chain[-1]["hash"]
}
block["hash"] = self.hash(block)
self.current_transcations=[]#交易被打包成區(qū)塊后,要清空
self.chain.append(block)
#新增一個(gè)新的交易
def new_transcations(self,sender:str,recer:str,amount:int)->int:
self.current_transcations.append({
"sender":sender,
"recer":recer,
"amount":amount,
})
return self.last_block()["index"]+1
# 獲得最后一個(gè)區(qū)塊
def last_block(self)->Dict[str,Any]:
return self.chain[-1]
# 整個(gè)區(qū)塊hash
@staticmethod
def hash(block:Dict[str,Any])->str:
blockstring = json.dumps(block,sort_keys=True).encode()
return hashlib.sha256(blockstring).hexdigest()
# 不斷修改值,進(jìn)行hash碰撞,起到算出結(jié)果為止(即拿到證明)
def proof_of_work(self,last_proof:int)->int:
proof = 0
while self.do_proof(last_proof,proof) is False:
proof+=1
return proof
# 進(jìn)行hash碰撞
@staticmethod
def do_proof(last_proof:int,proof:int)->bool:
guess=f'{last_proof*proof}'.encode()
guess_hash = hashlib.sha256(guess).hexdigest()
return guess_hash[:4]=="0000"#難度可調(diào)
# 校驗(yàn)整個(gè)鏈?zhǔn)欠裾_
def valid_chain(self,chain:List[Dict[str,Any]]):
#用當(dāng)前區(qū)塊與前一個(gè)區(qū)塊進(jìn)行校驗(yàn),循環(huán)至最后一個(gè)
pre_block = chain[0]
current_index = 1
while current_index<len(chain):
block = chain[current_index]
if block["pre_hash"]!=self.hash(pre_block): #校驗(yàn)是否被篡改,hash要能對上
return False
if not self.valid_proof(pre_block["proof"],block["proof"]):#校驗(yàn)工作量正確
return False
pre_block = block
current_index+=1
return True
# 節(jié)點(diǎn)注冊,追加節(jié)點(diǎn)
def register_node(self,addr:str)->None:
now_url=urlparse(addr)#將addr轉(zhuǎn)成對象,能取出schame或port等
self.nodes.add(now_url.netloc)#netloc='www.cwi.nl:80'
#節(jié)點(diǎn)同步:查找所有的節(jié)點(diǎn),如果附近的節(jié)點(diǎn)比自己多,將鄰居節(jié)點(diǎn)的鏈賦值給自己節(jié)點(diǎn)
def resolve_conflicts(self)->bool:
neighbours=self.nodes
new_chain=None
max_length=len(self.chain)
for node in neighbours:#從鄰居中取最長的鏈,賦值給new_chain
response = requests.get(f"http://{node}/chain") #node的域名與端口都不一樣
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
niucoin = NiuCoinBlockChainTwo()
node_id = str(uuid4()).replace("-","")
print("當(dāng)前節(jié)點(diǎn)錢包地址:",node_id)
app = Flask(__name__)
@app.route("/")
def index_page():
return "歡迎呵呵"
@app.route("/chain")
def chain():#輸出區(qū)塊鏈
response={
"chain":niucoin.chain,
"length":len(niucoin.chain),
}
return jsonify(response),200
@app.route("/mine")#挖礦,系統(tǒng)獎(jiǎng)勵(lì)比特幣
def index_mine():
last_block = niucoin.last_block()
last_proof = last_block["proof"]
proof = niucoin.proof_of_work(last_proof) #挖礦,挖到礦才往下走
niucoin.new_transcations("0",node_id,200) #獲得獎(jiǎng)勵(lì)
niucoin.new_block(None,proof)
response={
"message":"新的區(qū)塊產(chǎn)生",
"index":niucoin.last_block()["index"],
"transcations":niucoin.last_block()["transcations"],
"proof":niucoin.last_block()["proof"],
"pre_hash":niucoin.last_block()["pre_hash"]
}
return jsonify(response),200
@app.route("/new_transcations",methods=["POST"])#交易
def new_transcations():
values = request.get_json()
required = ["payer","recer","amount"]
if not all(key in values for key in required):
return "數(shù)據(jù)不完整",400
index = niucoin.new_transcations(values["payer"],values["recer"],values["amount"])
response = {
"message":f"交易加入到區(qū)塊{index}"
}
return jsonify(response),200
@app.route("/new_node",methods=["POST"])
def new_node():
values = request.get_json()
nodes = values.get("nodes")#獲得所有節(jié)點(diǎn)
if nodes is None:
return "數(shù)據(jù)有問題",400
for node in nodes:
niucoin.register_node(node) #加入節(jié)點(diǎn)
response = {
"message":f"節(jié)點(diǎn)已經(jīng)追加",
"node":list(niucoin.nodes)
}
return jsonify(response),200
@app.route("/node_refresh")
def node_refresh():
replaced = niucoin.resolve_conflicts() #最長的鏈
if replaced:
response={
"message":"區(qū)塊鏈查新為最長區(qū)塊鏈",
"new_chain":niucoin.chain
}
else:
response = {
"message": "區(qū)塊鏈已經(jīng)是最長的,無需替換",
"new_chain": niucoin.chain
}
return jsonify(response), 200
if __name__ == "__main__":
app.run("127.0.0.1",port=65533)
以上用到的庫(import),百度一下安裝即可;
端口要用5位,我這里四位不行,比如6000;
運(yùn)行時(shí)不要run flask,要配置python的運(yùn)行環(huán)境(在pycharm在右上角可配置flask還是python環(huán)境);
代碼比較長,先大概理解下代碼,跑完程序就理解所有代碼了;
有三個(gè)節(jié)點(diǎn)類,可以模擬三個(gè)節(jié)點(diǎn)信息同步,為了簡單我僅演示兩個(gè)。
1.準(zhǔn)備最長鏈,節(jié)點(diǎn)1
訪問『http://127.0.0.1:65531/mine』,執(zhí)行10次,制造最長鏈;
訪問『http://127.0.0.1:65531/chain』,可以顯示有11個(gè)區(qū)塊。
2.準(zhǔn)備短鏈,節(jié)點(diǎn)2
訪問『http://127.0.0.1:65532/mine』,執(zhí)行2次,制造最短鏈;
訪問『http://127.0.0.1:65532/chain』,可以顯示有3個(gè)區(qū)塊。
3.將所有節(jié)點(diǎn)注冊
用postman執(zhí)行『http://127.0.0.1:65532/new_node』,POST方式,內(nèi)容為『{"nodes":["127.0.0.1:65531","127.0.0.1:65533"]}』,加入節(jié)點(diǎn)1與節(jié)點(diǎn)3至節(jié)點(diǎn)2,這里僅是模擬,不要加入自己(節(jié)點(diǎn)2),否則會(huì)卡住線程。
4.同步信息,將短的替換為長的
訪問『http://127.0.0.1:65532/node_refresh』,同步最長鏈至當(dāng)前節(jié)點(diǎn);
至此就完成了簡單的節(jié)點(diǎn)間信息同步。