通過寫一個python區塊鏈原型程式學習區塊鏈
通過寫一個區塊鏈原型程式學習區塊鏈
網上看到一篇翻譯文章:
學習一門技術最好的方法就是自己做一個,水平有限,手敲了一遍這個程式碼,邊敲邊學,在這個過程中對區塊鏈有了更加全面的認識,同時也學習了一些python知識。
區塊鏈基本概念
區塊鏈是一個分散式賬本,每個參與挖礦的節點共同儲存全部交易資訊,沒有中心化認證機構,全網半數以上節點存活就可以保證交易資訊正確有效。
下面通過這個原型演示程式介紹一些區塊鏈的基本概念
1. 區塊(block):
相對完整的區塊包含區塊頭和區塊體,區塊頭儲存區塊的元資料,區塊體的內容是交易資訊記錄,區塊頭中包含交易資訊的Merkle樹雜湊,這個原型程式沒有區塊體,直接將區塊的元資料和交易資訊一起儲存在區塊裡。
· 索引:區塊序號
· 時間戳:區塊生成的時間
· 工作量證明:生成這個區塊耗費的工作量的證明
· 前一個區塊的雜湊:為了保證整個鏈條的合法性不被篡改,每個區塊儲存前一個區塊的雜湊值
本程式區塊:
block = {
'index': len(self.chain) + 1,
'timestamp': time(),
'transactions': self.current_transactions,
'proof': proof,
'previous_hash': previous_hash or self.hash
}
2. 區塊鏈(blockchain):
礦工通過挖礦把一段時間內的交易資訊固化在區塊內,每個區塊通過在本區塊儲存上一個區塊的雜湊值形成聯結關係,就像一條貪吃蛇,不斷生成區塊形成鏈條,就是區塊鏈了。
ew_block(self, proof, previous_hash=None): """ 生成一個新塊,新增進鏈 :param proof: <int> 工作量演算法給出的工作量證明 :param previous_hash: (optional) <str> 前一個塊的雜湊值 :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
3. 交易資訊(transaction):
區塊鏈應用的目的是維護一個賬本,也就是交易記錄,比如誰什麼時間給了誰多少錢。
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
4. 挖礦(mine):
交易隨時隨刻在發生,交易發生後交易資訊廣播給在區塊鏈程式裡註冊的每一個礦工,礦工通過挖礦工作生成區塊,礦工挖礦的目的就是封存交易資訊,只有新的區塊挖出來以後交易資訊才最終得到確認。挖礦需要消耗一定的時間,比特幣的規定時間是10分鐘,挖礦成功的時候,把從上一個區塊生成開始到挖出這個區塊的時間點之間(10分鐘)的交易資訊封存在這個新挖出的區塊裡,連進區塊鏈,並把交易記錄清零。因為挖礦需要耗費時間和裝置以及電力資源,挖出區塊的礦工會得到獎勵,礦工提供了工作量證明就能得到獎勵。
def mine(): # 計算工作量證明 last_block = blockchain.last_block last_proof = last_block['proof'] proof = blockchain.proof_of_work(last_proof) # 給工作量證明的節點提供獎勵 # 傳送者為0表示是新挖出的幣 blockchain.new_transaction( sender=0, recipient=node_identifier, amount=1, ) # 將新block加入chain block = blockchain.new_block(proof)
5. 工作量證明(POW):
proof of work,挖礦的工足量證明是通過計算一個字串雜湊值,使這個雜湊值滿足一定的條件。這個字串由前一個區塊的工作量證明和一個隨機陣列成,由於雜湊演算法是單向加密,因此只能通過不斷地試不同的隨機數來使組合字串的雜湊值滿足條件,通過對這個條件的苛刻程度進行調整可以控制挖礦的效率,即多長時間挖出一個區塊。比特幣使用sha256雜湊演算法計算工作量,本程式的POW條件為雜湊值前4位為0。
@staticmethod def proof_of_work(last_proof): """ 工作量證明: - 查詢一個p'使得hash(pp')以4個0開頭 - p是上一個塊的proof,p’是當前的proof :param last_proof: <int> :return: <int> """ proof = 0 while hashlib.sha256(f'{last_proof}{proof}'.encode()).hexdigest()[:4] != '0000': proof += 1 return proof
6. 分叉衝突(conflict)
每個礦工在自己的鏈尾新增新區塊,為了解決分散式系統的一致性問題,要解決分叉衝突。本程式每個礦工維護自己的鏈,解決衝突的方法是手工輪訓每個礦工維護的鏈,找到最長鏈,如果比自己的鏈長,就把自己的鏈替換為這個最長鏈。
def resolve_conflicts(self): """ 共識演算法解決衝突 使用網路中最長的鏈 :return: <bool> True如果鏈被取代,否則為False """ neighbours = self.nodes new_chain = None max_length = len(self.chain) # 抓取並驗證網路中所有節點的鏈 for node in neighbours: response = requests.get(f'http://{node}/chain') print(node) if response.status_code == 200: length = response.json()['length'] chain = response.json()['chain'] print(length) print(chain) print(self.valid_chain(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
區塊鏈執行機制
本程式使用python flask框架實現web介面,作為一個原型演示程式實現瞭如下功能:
1. 當有新的礦工節點加入工作網路,它把其他節點註冊進自己的節點列表並把自己註冊到其他全部礦工節點
2. 各節點可以隨時新增交易資訊,不過本程式未實現交易資訊的廣播
3. 礦工挖礦生成新區塊新增進自己的鏈
4. 輪訓查詢最長鏈替換自己的鏈
功能演示
在80節點註冊81和82節點
在81節點新增交易資訊
在81節點挖礦
在80節點解決衝突,鏈條被替換為81節點的鏈條
在82節點註冊80和81節點
在82節點解決衝突,鏈條被替換為最長鏈
本程式可以擴充的簡單功能
- 廣播交易資訊
- 挖礦前解決分叉衝突
- 加長POW計算時間
程式程式碼及執行方法
程式碼放在github,建議使用docker執行