区块链节点信息需要同步,本节主要讲一下选取最长的链做为基准链。
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:
# 构造器
def __init__(self):
self.current_transcations=[]#缓存交易列表,在没有新区块前放在这里,产生新区块后,会被加入到新区块
self.chain=[]#区块链管理多个区块
self.nodes=set()#保存网络中的其它节点
self.new_block(pre_hash="1",proof=100)#创世区块
#新增一个新区块
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,#将临时缓存的交易放在区块中
"proof":proof,#工作量要记下来,校验区块时需要这个证明
"pre_hash":pre_hash or self.chain[-1]["hash"]
}
block["hash"] = self.hash(block)
self.current_transcations=[]#交易被打包成区块后,要清空
self.chain.append(block)
#新增一个新的交易
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
# 获得最后一个区块
def last_block(self)->Dict[str,Any]:
return self.chain[-1]
# 整个区块hash
@staticmethod
def hash(block:Dict[str,Any])->str:
blockstring = json.dumps(block,sort_keys=True).encode()
return hashlib.sha256(blockstring).hexdigest()
# 不断修改值,进行hash碰撞,起到算出结果为止(即拿到证明)
def proof_of_work(self,last_proof:int)->int:
proof = 0
while self.do_proof(last_proof,proof) is False:
proof+=1
return proof
# 进行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"#难度可调
# 校验整个链是否正确
def valid_chain(self,chain:List[Dict[str,Any]]):
#用当前区块与前一个区块进行校验,循环至最后一个
pre_block = chain[0]
current_index = 1
while current_index<len(chain):
block = chain[current_index]
if block["pre_hash"]!=self.hash(pre_block): #校验是否被篡改,hash要能对上
return False
if not self.valid_proof(pre_block["proof"],block["proof"]):#校验工作量正确
return False
pre_block = block
current_index+=1
return True
# 节点注册,追加节点
def register_node(self,addr:str)->None:
now_url=urlparse(addr)#将addr转成对象,能取出schame或port等
self.nodes.add(now_url.netloc)#netloc='www.cwi.nl:80'
#节点同步:查找所有的节点,如果附近的节点比自己多,将邻居节点的链赋值给自己节点
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("当前节点钱包地址:",node_id)
app = Flask(__name__)
@app.route("/")
def index_page():
return "欢迎呵呵"
@app.route("/chain")
def chain():#输出区块链
response={
"chain":niucoin.chain,
"length":len(niucoin.chain),
}
return jsonify(response),200
@app.route("/mine")#挖矿,系统奖励比特币
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) #获得奖励
niucoin.new_block(None,proof)
response={
"message":"新的区块产生",
"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 "数据不完整",400
index = niucoin.new_transcations(values["payer"],values["recer"],values["amount"])
response = {
"message":f"交易加入到区块{index}"
}
return jsonify(response),200
@app.route("/new_node",methods=["POST"])#新增一个节点
def new_node():
values = request.get_json()
nodes = values.get("nodes")#获得所有节点
if nodes is None:
return "数据有问题",400
for node in nodes:
niucoin.register_node(node) #加入节点
response = {
"message":f"节点已经追加",
"node":list(niucoin.nodes)
}
return jsonify(response),200
@app.route("/node_synchronize")#节点信息同步
def node_refresh():
replaced = niucoin.resolve_conflicts() #最长的链
if replaced:
response={
"message":"区块链查新为最长区块链",
"new_chain":niucoin.chain
}
else:
response = {
"message": "区块链已经是最长的,无需替换",
"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:
#构造器
def __init__(self):
self.current_transcations=[]#缓存交易列表,在没有新区块前放在这里,产生新区块后,会被加入到新区块
self.chain=[]#区块链管理多个区块
self.nodes=set()#保存网络中的其它节点
self.new_block(pre_hash="1",proof=100)#创世区块
#新增一个新区块
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,#将临时缓存的交易放在区块中
"proof":proof,#工作量要记下来,校验区块时需要这个证明
"pre_hash":pre_hash or self.chain[-1]["hash"]
}
block["hash"] = self.hash(block)
self.current_transcations=[]#交易被打包成区块后,要清空
self.chain.append(block)
#新增一个新的交易
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
# 获得最后一个区块
def last_block(self)->Dict[str,Any]:
return self.chain[-1]
# 整个区块hash
@staticmethod
def hash(block:Dict[str,Any])->str:
blockstring = json.dumps(block,sort_keys=True).encode()
return hashlib.sha256(blockstring).hexdigest()
# 不断修改值,进行hash碰撞,起到算出结果为止(即拿到证明)
def proof_of_work(self,last_proof:int)->int:
proof = 0
while self.do_proof(last_proof,proof) is False:
proof+=1
return proof
# 进行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"#难度可调
# 校验整个链是否正确
def valid_chain(self,chain:List[Dict[str,Any]]):
#用当前区块与前一个区块进行校验,循环至最后一个
pre_block = chain[0]
current_index = 1
while current_index<len(chain):
block = chain[current_index]
if block["pre_hash"]!=self.hash(pre_block): #校验是否被篡改,hash要能对上
return False
if not self.valid_proof(pre_block["proof"],block["proof"]):#校验工作量正确
return False
pre_block = block
current_index+=1
return True
# 节点注册,追加节点
def register_node(self,addr:str)->None:
now_url=urlparse(addr)#将addr转成对象,能取出schame或port等
self.nodes.add(now_url.path)#netloc='www.cwi.nl:80'
#节点同步:查找所有的节点,如果附近的节点比自己多,将邻居节点的链赋值给自己节点
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("当前节点钱包地址:",node_id)
app = Flask(__name__)
@app.route("/")
def index_page():
return "欢迎呵呵"
@app.route("/chain")
def chain():#输出区块链
response={
"chain":niucoin.chain,
"length":len(niucoin.chain),
}
return jsonify(response),200
@app.route("/mine")#挖矿,系统奖励比特币
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) #获得奖励
niucoin.new_block(None,proof)
response={
"message":"新的区块产生",
"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 "数据不完整",400
index = niucoin.new_transcations(values["payer"],values["recer"],values["amount"])
response = {
"message":f"交易加入到区块{index}"
}
return jsonify(response),200
@app.route("/new_node",methods=["POST"])
def new_node():
values = request.get_json()
nodes = values.get("nodes")#获得所有节点
if nodes is None:
return "数据有问题",400
for node in nodes:
niucoin.register_node(node) #加入节点
response = {
"message":f"节点已经追加",
"node":list(niucoin.nodes)
}
return jsonify(response),200
@app.route("/node_refresh")
def node_refresh():
replaced = niucoin.resolve_conflicts() #最长的链
if replaced:
response={
"message":"区块链查新为最长区块链",
"new_chain":niucoin.chain
}
else:
response = {
"message": "区块链已经是最长的,无需替换",
"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:
#构造器
def __init__(self):
self.current_transcations=[]#缓存交易列表,在没有新区块前放在这里,产生新区块后,会被加入到新区块
self.chain=[]#区块链管理多个区块
self.nodes=set()#保存网络中的其它节点
self.new_block(pre_hash="1",proof=100)#创世区块
#新增一个新区块
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,#将临时缓存的交易放在区块中
"proof":proof,#工作量要记下来,校验区块时需要这个证明
"pre_hash":pre_hash or self.chain[-1]["hash"]
}
block["hash"] = self.hash(block)
self.current_transcations=[]#交易被打包成区块后,要清空
self.chain.append(block)
#新增一个新的交易
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
# 获得最后一个区块
def last_block(self)->Dict[str,Any]:
return self.chain[-1]
# 整个区块hash
@staticmethod
def hash(block:Dict[str,Any])->str:
blockstring = json.dumps(block,sort_keys=True).encode()
return hashlib.sha256(blockstring).hexdigest()
# 不断修改值,进行hash碰撞,起到算出结果为止(即拿到证明)
def proof_of_work(self,last_proof:int)->int:
proof = 0
while self.do_proof(last_proof,proof) is False:
proof+=1
return proof
# 进行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"#难度可调
# 校验整个链是否正确
def valid_chain(self,chain:List[Dict[str,Any]]):
#用当前区块与前一个区块进行校验,循环至最后一个
pre_block = chain[0]
current_index = 1
while current_index<len(chain):
block = chain[current_index]
if block["pre_hash"]!=self.hash(pre_block): #校验是否被篡改,hash要能对上
return False
if not self.valid_proof(pre_block["proof"],block["proof"]):#校验工作量正确
return False
pre_block = block
current_index+=1
return True
# 节点注册,追加节点
def register_node(self,addr:str)->None:
now_url=urlparse(addr)#将addr转成对象,能取出schame或port等
self.nodes.add(now_url.netloc)#netloc='www.cwi.nl:80'
#节点同步:查找所有的节点,如果附近的节点比自己多,将邻居节点的链赋值给自己节点
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("当前节点钱包地址:",node_id)
app = Flask(__name__)
@app.route("/")
def index_page():
return "欢迎呵呵"
@app.route("/chain")
def chain():#输出区块链
response={
"chain":niucoin.chain,
"length":len(niucoin.chain),
}
return jsonify(response),200
@app.route("/mine")#挖矿,系统奖励比特币
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) #获得奖励
niucoin.new_block(None,proof)
response={
"message":"新的区块产生",
"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 "数据不完整",400
index = niucoin.new_transcations(values["payer"],values["recer"],values["amount"])
response = {
"message":f"交易加入到区块{index}"
}
return jsonify(response),200
@app.route("/new_node",methods=["POST"])
def new_node():
values = request.get_json()
nodes = values.get("nodes")#获得所有节点
if nodes is None:
return "数据有问题",400
for node in nodes:
niucoin.register_node(node) #加入节点
response = {
"message":f"节点已经追加",
"node":list(niucoin.nodes)
}
return jsonify(response),200
@app.route("/node_refresh")
def node_refresh():
replaced = niucoin.resolve_conflicts() #最长的链
if replaced:
response={
"message":"区块链查新为最长区块链",
"new_chain":niucoin.chain
}
else:
response = {
"message": "区块链已经是最长的,无需替换",
"new_chain": niucoin.chain
}
return jsonify(response), 200
if __name__ == "__main__":
app.run("127.0.0.1",port=65533)
以上用到的库(import),百度一下安装即可;
端口要用5位,我这里四位不行,比如6000;
运行时不要run flask,要配置python的运行环境(在pycharm在右上角可配置flask还是python环境);
代码比较长,先大概理解下代码,跑完程序就理解所有代码了;
有三个节点类,可以模拟三个节点信息同步,为了简单我仅演示两个。
1.准备最长链,节点1
访问『http://127.0.0.1:65531/mine』,执行10次,制造最长链;
访问『http://127.0.0.1:65531/chain』,可以显示有11个区块。
2.准备短链,节点2
访问『http://127.0.0.1:65532/mine』,执行2次,制造最短链;
访问『http://127.0.0.1:65532/chain』,可以显示有3个区块。
3.将所有节点注册
用postman执行『http://127.0.0.1:65532/new_node』,POST方式,内容为『{"nodes":["127.0.0.1:65531","127.0.0.1:65533"]}』,加入节点1与节点3至节点2,这里仅是模拟,不要加入自己(节点2),否则会卡住线程。
4.同步信息,将短的替换为长的
访问『http://127.0.0.1:65532/node_refresh』,同步最长链至当前节点;
至此就完成了简单的节点间信息同步。