歡迎光臨
每天分享高質量文章

看完此文再不懂區塊鏈算我輸,用Python從零開始建立區塊鏈

來源:51CTO技術棧,網際網路綜合整理

  • 笑噴!區塊鏈十個段子集錦》轉載自劉興亮時間微信公眾號

  • 圖解:大白話瞭解到底區塊鏈是個啥?》轉載自黑馬程式員影片庫微信公眾號

  • 用Python從零開始建立區塊鏈作者:Daniel van Flymen,Tiny熊譯。

    原文:https://learnblockchain.cn/2017/10/27/build_blockchain_by_python/

    原始碼:https://github.com/xilibi2003/blockchain

如果你還沒有聽說過 3 點鐘區塊鏈群,說明你還不是鏈圈的人;如果你還沒有加入 3 點鐘區塊鏈群,說明你還不是鏈圈的大佬;如果你還沒有被 3 點鐘區塊鏈群刷屏,說明你還體會不到什麼是“幣圈一天,人間一年”。

三點鐘區塊鏈”無疑成為了大家春節期間焦慮的根源,而“區塊鏈”註定是 2018 年被持續討論、關註的行業性熱點話題。

3 月 1 日,朱嘯虎對正翻湧不斷的區塊鏈熱潮再次開炮,在朋友圈一張畫滿區塊鏈應用的圖上,朱嘯虎質疑:所有這些應用加在一起,有多少日活使用者?“2000 年的網際網路泡沫至少還有 eyeball,今天的區塊鏈除了炒幣外還有什麼”?

在此之前,朱嘯虎在朋友圈轉發了諷刺區塊鏈投資熱的文章《來,喝了這碗區塊鏈的毒雞湯!》,並宣告:“不要拉我進任何 3 點鐘群,有些風口寧願錯過,有些錢寧願不賺,大家晚節保重。”朱嘯虎還表示,說 ICO 是龐氏騙局是在侮辱龐氏騙局。

作為程式員的你,再不懂這個技術,2018 可能會被淘汰!下麵和小編一起從十個幽默段子入門區塊鏈吧!

笑噴!區塊鏈十個段子集錦

1、假如你是一位女性,你男朋友每次跟你說一句肉麻的話或者承諾給你買東西,你都立燒錄下來並且發給你的和他的所有閨蜜、同學、同事,還有各種群和朋友圈,讓他再也無法抵賴,這叫區塊鏈。

2、麻將是中國傳統的區塊鏈專案:四個礦工一組,先碰撞出 13 個數字正確雜湊值的礦工可以獲得記賬權並得到獎勵。不可篡改。因為說服其他三個人需要消耗太多算力和體力。

3、玩夜店的小姐姐和玩虛擬幣的小哥哥們有幾處相似:

  • 都是自認聰明的優秀群體

  • 不給自己賺錢的都是傻 X 屌絲

  • 都認識很多大佬

  • 都明白很多道理

  • 都是在等自己漲價或者自己的虛擬幣漲價被別人接盤

4、區塊鏈是正經技術,各種幣正不正經就不知道了。

5、吳三桂在山海關沖冠一怒,本質是為了爭奪睡陳圓圓的權力;大佬們在區塊鏈路上的互懟,本質是為了爭奪割韭菜的權力。

6、新學期剛開始,兒子問老爸:「父親工作一欄怎麼填?是寫幣民嗎?」老爸猶豫了一下說:「就寫多家上市公司股東。」

7、最近數字貨幣很火,很多山寨幣都是幾倍幾十倍的增長。很多炒幣的開始飄飄然,叫囂什麼「一幣一嫩模」。有朋友就問我要不要跟?我觀點很簡單:淘金熱,一窩蜂淘金,風險很大。所以,讓他們叫「一幣一嫩模」去吧,不要跟風盲目炒幣,我們應該賺他們的錢——去當嫩模!當嫩模!嫩模!

8、我昨天遇見一幣友,問他:「近來幣市暴降,睡覺質量怎麼樣?」

他說:「還行,像嬰兒般睡覺!」

我說:「羨慕了。」

他說:「是睡一個小時,醒了,然後哭一個小時,接著,再睡一個小時,起來再哭一個小時。」

9、老同志語重心長地對 80、90 後說:「別玩那些比特幣,那些虛擬的玩意,做點實事在北京買個房、娶個媳婦,多好!」90 後回答說:「你們都把幾千塊錢成本的房子搞到10萬一平米了。我們不另尋出路,搞一串串數字 10 萬一個賣給你們,我們拿什麼買得起房子啊?」

10、首先感謝公司拿出價值 100 萬的比特幣作為給員工的獎勵,其次我覺得自己很幸運能拿到這 95 萬的獎勵,然後我覺得我還是要好好規劃一下這 86 萬的用處,畢竟 70 萬也不是一筆小錢,我打算拿出 20 萬給父母,剩下的 36 萬暫時還沒想好怎麼用,總之,感謝公司價值 30 萬的比特幣的獎勵,謝謝,祝大家和我一樣都能得到這 15 萬的獎勵。

圖解:大白話瞭解到底區塊鏈是個啥?

what is 區塊鏈

“區塊鏈僅僅是一門技術而已”,“比特幣”僅僅是區塊鏈技術的一種應用而已, 就好比,一個人會廚藝的技術,但是應用起他的廚藝可以做出“宮保雞丁”、”魚香肉絲”等各式的菜餚。

那麼,到底“區塊鏈”是個啥?我們這裡藉助網上一個比較流行的段子,將它用圖形的形式展示給大家。

我們可以將“比特幣”抽象成“某榮的照片”如上圖所示,如果網上很多使用者想要得到某榮的照片,需要到一個固定的網站去搜索。

當然你也沒其他地方可去嘛,那麼好了,這個某榴網站天天給你彈出廣告啊,小視窗啊,你都得忍著,沒辦法,因為就這一個地方可以獲取嘛。

再者,這個網站突然被警察叔叔封殺了咋辦?或者斷電?斷網?管他啥的,反正就是伺服器崩潰了,那麼悲劇的“某榮”粉絲們心愛的 2100 張照片將全部失去提供資源的場所。

這就是網上流傳的新詞“中心化”嘍,他的弊端就是資源集中一起,抗風險容錯性很弱。資源容易丟失。

那麼,怎麼解決這個問題呢?我們試想,能不能讓每個“某榮”粉絲,都擁有這 2100 張照片呢?比如下圖:

這樣的話,貌似就不用在依賴那個“某榴”網站了呢。即使某個粉絲突然電腦崩潰,他隨便找一個其他粉絲來獲得這 2100 張照片,不愁了,不愁了。

後來,一個叫“某本聰”的一個虛擬人物提供了,一個擁有協議的某榮照片共享檔案夾,使用者可以從中獲取照片,但是必須遵循協議。

這樣,每個粉絲都可以從這個檔案夾中獲取那 2100 張某榮的照片,但是全部獲取的粉絲必須要遵循一個協議,當然是人家某本聰定義的協議啦。

“不得複製,修改,共享檔案中的任意照片,粉絲們在共享檔案夾中的任何行為都會被記錄,並且是按照時間去記錄!”

粉絲們這麼喜愛某榮,而且不需要去某榴獲取,當然就紛紛踴躍加入了~

忽然,有一天,調皮的小 One 想要違背規則,在 2018 年 1 月 15 日中午 12 點刪除編號為 1-100 的某榮照片。

根據協議,這個行為會被記錄,並且會廣播給其他粉絲。

照片到底被刪除了麼?當然不是,因為小露手裡也有照片嘛,她收到廣播後可以立刻恢復共享檔案夾中已經刪除的照片,小 One 永遠別想對“共享檔案夾”搞修改破壞,且所有行為都同步記錄在其他使用者的電腦裡。

這就是區塊鏈,資料分散儲存,去中心化。按時間戳廣播記錄所有行為,無法修改、破壞資料源或造假。

   

除非同一時刻炸掉 100 萬個使用者的電腦,或網際網路消失,或世界毀滅….否則資料將永遠存在~~

如何增加區塊鏈保護的資源?“某本聰”又來了。他說,你們是可以在檔案中新增某榮照片的,但是呢,你們各位必須達到某種“共識”?啥是“共識”,就是我們都承認的規則嘍。

那到底是個啥共識呢?啊,小 One 和小露立刻瞭解了某本聰的意思,在每年規定的時間內,儘快拍出 100 張某榮的照片,這樣就可以新增到“某榮共享檔案夾”了,我們的資源就擴充了。

但是,好景不長。某本聰發現大量的拍照,衝擊前 100,那很快就能達成了,拍照就沒有難度了。

而且照片質量還差,好像誰都能輕易的新增照片資源,這樣就保證不了某榮的照片質量了。於是,某本聰再次降臨,增加拍照的共識難度。 

小 One 和小露作為忠實的粉絲,怎能放棄,他們買了高階相機,讓某榮擺出各種姿勢,花費大量的時間和汗水完成了高質量的照片,當然自己也非常的辛苦。

這樣高質量的照片就可以新增到“某榮的檔案夾”中了。

何為 ICO?

小 One 想:每張照片都是不可造假破壞的,所以具有唯一性,還有單獨編號,給每一張照片估價,它不就值錢了嗎?就像現實世界中無法複製的名畫一樣!

小 One 就把之前的某榮照片,構造出來相應的“某榮幣”,當然這個行為就向我們的政府透過國庫的黃金數額發行等價的人民幣類似啦。

估值呢,當然小 One 說的算啦,人家是照片的擁有者嘛。

為了證實某榮幣 5W 塊一個,小 One 首先購買了 2100 個中的 1100 個,剩下的發行給吃瓜群眾們啦,我們已經用 5W 買一個,說明他已經值這個價錢啦,剩下 1000 個大家一起買吧。

這樣的話如果 2100 個全部認購出去,2100 個某榮幣可以就估值 1.05 億塊哦~這種透過數字貨幣的發行而得到融資的過程就是 ICO 啦。

根據這個圖的含義,如果小 One 和小露是一個可信任的機構或者公眾人物,還是可以相信他們的,當然啦也會有很多不法分子惡意發行貨幣來套現的。

這也是我們國家為什麼禁止 ICO 發行的原因啦,因為目前沒有一個完善的 ICO 監管條例能夠保證發行貨幣的機構的可信任性和合法的監管他們,所以吃瓜群眾們要自己承擔風險找到一個可信的機構。

這樣 2100 個某榮幣全部認購成功,基金成立,這就是 ICO。當然吃瓜群眾也可以繼續拍照創造某榮幣,就是有點難罷了。

現在各位瞭解什麼是區塊鏈和 ICO 了吧~下麵手把手教你如何用 Python 語言建立一個區塊鏈?

用 Python 從 0 開始建立一個區塊鏈

對數字貨幣的崛起感到新奇的我們,並且想知道其背後的技術——區塊鏈是怎樣實現的。本文透過 Python 構建一個區塊鏈可以加深對區塊鏈的理解。

準備工作

本文要求讀者對 Python 有基本的理解,能讀寫基本的 Python,並且需要對 HTTP 請求有基本的瞭解。

我們知道區塊鏈是由區塊的記錄構成的不可變、有序的鏈結構,記錄可以是交易、檔案或任何你想要的資料,重要的是它們是透過雜湊值(hashes)連結起來的。

如果你還不是很瞭解雜湊,可以檢視這篇文章https://learncryptography.com/hash-functions/what-are-hash-functions。

環境準備:

環境準備,確保已經安裝 Python3.6+、pip、Flask、requests。

安裝方法:

pip install Flask==0.12.2 requests==2.18.4

同時還需要一個 HTTP 客戶端,比如 Postman、cURL 或其他客戶端。

參考原始碼(原始碼在我翻譯的時候,無法執行,我 fork 了一份,修複了其中的錯誤,並添加了翻譯,感謝 star)。

開始建立 Blockchain

新建一個檔案 blockchain.py,本文所有的程式碼都寫在這一個檔案中,可以隨時參考原始碼。

Blockchain 類

首先建立一個 Blockchain 類,在建構式中建立了兩個串列,一個用於儲存區塊鏈,一個用於儲存交易。

以下是 Blockchain 類的框架:

class Blockchain(object):
   def __init__(self):
       self.chain = []
       self.current_transactions = []
   def new_block(self):
       # Creates a new Block and adds it to the chain
       pass
   def new_transaction(self):
       # Adds a new transaction to the list of transactions
       pass
   @staticmethod
   def hash(block):
       # Hashes a Block
       pass
   @property
   def last_block(self):
       # Returns the last Block in the chain
       pass

Blockchain 類用來管理鏈條,它能儲存交易、加入新塊等,下麵我們來進一步完善這些方法。

塊結構

每個區塊包含屬性:索引(index)、Unix 時間戳(timestamp)、交易串列(transactions)、工作量證明(稍後解釋)以及前一個區塊的 Hash 值。

以下是一個區塊的結構:

block = {
   'index': 1,
   'timestamp': 1506057125.900785,
   'transactions': [
       {
           'sender': "8527147fe1f5426f9dd545de4b27ee00",
           'recipient': "a77f5cdfa2934df3954a5c7c7da5df1f",
           'amount': 5,
       }
   ],
   'proof': 324984774000,
   'previous_hash': "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
}

到這裡區塊鏈的概念就清楚了,每個新的區塊都包含上一個區塊的 Hash,這是關鍵的一點,它保障了區塊鏈的不可變性。

如果攻擊者破壞了前面的某個區塊,那麼後面所有區塊的Hash都會變得不正確。不理解的話,慢慢消化,可參考{% post_link whatbc 區塊鏈技術原理 %}。

加入交易

接下來我們需要新增一個交易,來完善下 new_transaction 方法:

class Blockchain(object):
   ...
   def new_transaction(self, sender, recipient, amount):
       """
       生成新交易資訊,資訊將加入到下一個待挖的區塊中
       :param sender: Address of the Sender
       :param recipient: Address of the Recipient
       :param amount: Amount
       :return: The index of the Block that will hold this transaction
       """

       self.current_transactions.append({
           'sender': sender,
           'recipient': recipient,
           'amount': amount,
       })
       return self.last_block['index'] + 1

方法向串列中新增一個交易記錄,並傳回該記錄將被新增到的區塊(下一個待挖掘的區塊)的索引,等下在使用者提交交易時會有用。

建立新塊

當 Blockchain 實體化後,我們需要構造一個創世塊(沒有前區塊的第一個區塊),並且給它加上一個工作量證明。每個區塊都需要經過工作量證明,俗稱挖礦,稍後會繼續講解。

為了構造創世塊,我們還需要完善 new_block(),new_transaction() 和hash() 方法:

import hashlib
import json
from time import time
class Blockchain(object):
   def __init__(self):
       self.current_transactions = []
       self.chain = []
       # Create the genesis block
       self.new_block(previous_hash=1, proof=100)
   def new_block(self, proof, previous_hash=None):
       """
       生成新塊
       :param proof: The proof given by the Proof of Work algorithm
       :param previous_hash: (Optional) Hash of previous Block
       :return: New Block
       """

       block = {
           'index': len(self.chain) + 1,
           'timestamp': time(),
           'transactions': self.current_transactions,
           'proof': proof,
           'previous_hash': previous_hash or self.hash(self.chain[-1]),
       }
       # Reset the current list of transactions
       self.current_transactions = []
       self.chain.append(block)
       return block
   def new_transaction(self, sender, recipient, amount):
       """
       生成新交易資訊,資訊將加入到下一個待挖的區塊中
       :param sender: Address of the Sender
       :param recipient: Address of the Recipient
       :param amount: Amount
       :return: The index of the Block that will hold this transaction
       """

       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 hash值
       :param block: Block
       :return:
       """

       # We must make sure that the Dictionary is Ordered, or we'll have inconsistent hashes
       block_string = json.dumps(block, sort_keys=True).encode()
       return hashlib.sha256(block_string).hexdigest()

透過上面的程式碼和註釋可以對區塊鏈有直觀的瞭解,接下來我們看看區塊是怎麼挖出來的。

理解工作量證明

新的區塊依賴工作量證明演演算法(PoW)來構造。PoW 的標的是找出一個符合特定條件的數字,這個數字很難計算出來,但容易驗證。這就是工作量證明的核心思想。

為了方便理解,舉個例子:

假設一個整數 x 乘以另一個整數 y 的積的 Hash 值必須以 0 結尾,即 hash(x * y) = ac23dc…0。設變數 x = 5,求 y 的值?

用 Python 實現如下:

from hashlib import sha256
x = 5
y = 0  # y未知
while sha256(f'{x*y}'.encode()).hexdigest()[-1] != "0":
   y += 1
print(f'The solution is y = {y}')

結果是:y = 21,因為:

hash(5 * 21) = 1253e9373e...5e3600155e860

在比特幣中,使用稱為 Hashcash 的工作量證明演演算法,它和上面的問題很類似,礦工們為了爭奪建立區塊的權利而爭相計算結果。

通常,計算難度與標的字串需要滿足的特定字元的數量成正比,礦工算出結果後,會獲得比特幣獎勵。當然,在網路上非常容易驗證這個結果。

實現工作量證明

讓我們來實現一個相似 PoW 演演算法,規則是:尋找一個數 p,使得它與前一個區塊的 proof 拼接成的字串的 Hash 值以 4 個零開頭。

import hashlib
import json
from time import time
from uuid import uuid4
class Blockchain(object):
   ...
   def proof_of_work(self, last_proof):
       """
       簡單的工作量證明:
        - 查詢一個 p' 使得 hash(pp') 以4個0開頭
        - p 是上一個塊的證明,  p' 是當前的證明
       :param last_proof:
       :return:
       """

       proof = 0
       while self.valid_proof(last_proof, proof) is False:
           proof += 1
       return proof
   @staticmethod
   def valid_proof(last_proof, proof):
       """
       驗證證明: 是否hash(last_proof, proof)以4個0開頭?
       :param last_proof: Previous Proof
       :param proof: Current Proof
       :return: True if correct, False if not.
       """

       guess = f'{last_proof}{proof}'.encode()
       guess_hash = hashlib.sha256(guess).hexdigest()
       return guess_hash[:4] == "0000"

衡量演演算法複雜度的辦法是修改零開頭的個數。使用 4 個零來用於演示,你會發現多一個零都會大大增加計算出結果所需的時間。

現在 Blockchain 類基本已經完成了,接下來使用 HTTP requests 來進行互動。

Blockchain 作為 API 介面

我們將使用 Python Flask 框架,這是一個輕量 Web 應用框架,它方便將網路請求對映到 Python 函式,現在我們來讓 Blockchain 執行在 Flask Web 上。

我們將建立三個介面:

  • /transactions/new 建立一個交易並新增到區塊

  • /mine 告訴伺服器去挖掘新的區塊

  • /chain 傳回整個區塊鏈

建立節點

我們的“Flask 伺服器”將扮演區塊鏈網路中的一個節點,我們先新增一些框架程式碼:

import hashlib
import json
from textwrap import dedent
from time import time
from uuid import uuid4
from flask import Flask
class Blockchain(object):
   ...
# Instantiate our Node
app = Flask(__name__)
# Generate a globally unique address for this node
node_identifier = str(uuid4()).replace('-', '')
# Instantiate the Blockchain
blockchain = Blockchain()
@app.route('/mine', methods=['GET'])
def mine():
   return "We'll mine a new Block"
@app.route('/transactions/new', methods=['POST'])
def new_transaction():
   return "We'll add a new transaction"
@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)

簡單的說明一下以上程式碼:

  • 第 15 行:建立一個節點。

  • 第 18 行:為節點建立一個隨機的名字。

  • 第 21 行:實體 Blockchain 類。

  • 第 24–26 行:建立 /mine GET 介面。

  • 第 28–30 行:建立 /transactions/new POST 介面,可以給介面傳送交易資料。

  • 第 32–38 行:建立 /chain 介面, 傳回整個區塊鏈。

  • 第 40–41 行:服務執行在埠 5000 上。

傳送交易

傳送到節點的交易資料結構如下:

{
"sender": "my address",
"recipient": "someone else's address",
"amount": 5
}

之前已經有新增交易的方法,基於介面來新增交易就很簡單了:

import hashlib
import json
from textwrap import dedent
from time import time
from uuid import uuid4
from flask import Flask, jsonify, request
...
@app.route('/transactions/new', methods=['POST'])
def new_transaction():
   values = request.get_json()
   # Check that the required fields are in the POST'ed data
   required = ['sender', 'recipient', 'amount']
   if not all(k in values for k in required):
       return 'Missing values', 400
   # Create a new Transaction
   index = blockchain.new_transaction(values['sender'], values['recipient'], values['amount'])
   response = {'message': f'Transaction will be added to Block {index}'}
   return jsonify(response), 201

挖礦

挖礦正是神奇所在,它很簡單,做了以下三件事:

  • 計算工作量證明 PoW。

  • 透過新增一個交易授予礦工(自己)一個幣。

  • 構造新區塊並將其新增到鏈中。

import hashlib
import json
from textwrap import dedent
from time import time
from uuid import uuid4
from flask import Flask, jsonify, request
...
import hashlib
import json
from time import time
from uuid import uuid4
from flask import Flask, jsonify, request
...
@app.route('/mine', methods=['GET'])
def mine():
   # We run the proof of work algorithm to get the next proof...
   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,
   )
   # Forge the new Block by adding it to the chain
   block = blockchain.new_block(proof)
   response = {
       'message': "New Block Forged",
       'index': block['index'],
       'transactions': block['transactions'],
       'proof': block['proof'],
       'previous_hash': block['previous_hash'],
   }
   return jsonify(response), 200

註意交易的接收者是我們自己的伺服器節點,我們做的大部分工作都只是圍繞 Blockchain 類方法進行互動。到此,我們的區塊鏈就算完成了,我們來實際執行下。

執行區塊鏈

你可以使用 cURL 或 Postman 去和 API 進行互動。

啟動 server:

$ python blockchain.py
* Runing on http://127.0.0.1:5000/ (Press CTRL+C to quit)

讓我們透過請求 http://localhost:5000/mine 來進行挖礦:

透過 post 請求,新增一個新交易:

如果不是使用 Postman,則用以下的 cURL 陳述句也是一樣的:

$ curl -X POST -H "Content-Type: application/json" -d '{
"sender": "d4ee26eee15148ee92c6cd394edd974e",
"recipient": "someone-other-address",
"amount": 5
}' "http://localhost:5000/transactions/new"

在挖了兩次礦之後,就有 3 個塊了,透過請求 http://localhost:5000/chain 可以得到所有的塊資訊。

{
 "chain": [
   {
     "index": 1,
     "previous_hash": 1,
     "proof": 100,
     "timestamp": 1506280650.770839,
     "transactions": []
   },
   {
     "index": 2,
     "previous_hash": "c099bc...bfb7",
     "proof": 35293,
     "timestamp": 1506280664.717925,
     "transactions": [
       {
         "amount": 1,
         "recipient": "8bbcb347e0634905b0cac7955bae152b",
         "sender": "0"
       }
     ]
   },
   {
     "index": 3,
     "previous_hash": "eff91a...10f2",
     "proof": 35089,
     "timestamp": 1506280666.1086972,
     "transactions": [
       {
         "amount": 1,
         "recipient": "8bbcb347e0634905b0cac7955bae152b",
         "sender": "0"
       }
     ]
   }
 ],
 "length": 3
}

一致性(共識)

我們已經有了一個基本的區塊鏈可以接受交易和挖礦,但是區塊鏈系統應該是分散式的。

既然是分散式的,那麼我們究竟拿什麼保證所有節點有同樣的鏈呢?這就是一致性問題,我們要想在網路上有多個節點,就必須實現一個一致性的演演算法。

註冊節點

在實現一致性演演算法之前,我們需要找到一種方式讓一個節點知道它相鄰的節點。

每個節點都需要儲存一份包含網路中其他節點的記錄,因此讓我們新增幾個介面:

  • /nodes/register 接收 URL 形式的新節點串列。

  • /nodes/resolve 執行一致性演演算法,解決任何衝突,確保節點擁有正確的鏈。

我們修改下 Blockchain 的 init 函式並提供一個註冊節點方法:

...
from urllib.parse import urlparse
...
class Blockchain(object):
   def __init__(self):
       ...
       self.nodes = set()
       ...
   def register_node(self, address):
       """
       Add a new node to the list of nodes
       :param address: Address of node. Eg. 'http://192.168.0.5:5000'
       :return: None
       """

       parsed_url = urlparse(address)
       self.nodes.add(parsed_url.netloc)

我們用 set 來儲存節點,這是一種避免重覆新增節點的簡單方法。

實現共識演演算法

前面提到,衝突是指不同的節點擁有不同的鏈,為瞭解決這個問題,規定最長的、有效的鏈才是最終的鏈,換句話說,網路中有效最長鏈才是實際的鏈。

我們使用以下的演演算法,來達到網路中的共識:

...
import requests
class Blockchain(object)
   ...
   def valid_chain(self, chain):
       """
       Determine if a given blockchain is valid
       :param chain: A blockchain
       :return: True if valid, False if not
       """

       last_block = chain[0]
       current_index = 1
       while current_index < len(chain):
           block = chain[current_index]
           print(f'{last_block}')
           print(f'{block}')
           print("-----------")
           # Check that the hash of the block is correct
           if block['previous_hash'] != self.hash(last_block):
               return False
           # Check that the Proof of Work is correct
           if not self.valid_proof(last_block['proof'], block['proof']):
               return False
           last_block = block
           current_index += 1
       return True
   def resolve_conflicts(self):
       """
       共識演演算法解決衝突
       使用網路中最長的鏈.
       :return: True 如果鏈被取代, 否則為False
       """

       neighbours = self.nodes
       new_chain = None
       # We're only looking for chains longer than ours
       max_length = len(self.chain)
       # Grab and verify the chains from all the nodes in our network
       for node in neighbours:
           response = requests.get(f'http://{node}/chain')
           if response.status_code == 200:
               length = response.json()['length']
               chain = response.json()['chain']
               # Check if the length is longer and the chain is valid
               if length > max_length and self.valid_chain(chain):
                   max_length = length
                   new_chain = chain
       # Replace our chain if we discovered a new, valid chain longer than ours
       if new_chain:
           self.chain = new_chain
           return True
       return False

第一個方法 valid_chain() 用來檢查是否是有效鏈,遍歷每個塊驗證 hash 和 proof。

第二個方法 resolve_conflicts() 用來解決衝突,遍歷所有的鄰居節點,並用上一個方法檢查鏈的有效性, 如果發現有效更長鏈,就替換掉自己的鏈。

讓我們新增兩個路由,一個用來註冊節點,一個用來解決衝突。

@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

你可以在不同的機器執行節點,或在一臺機機開啟不同的網路埠來模擬多節點的網路。

這裡在同一臺機器開啟不同的埠演示,在不同的終端執行以下命令,就啟動了兩個節點:

  • http://localhost:5000 

  • http://localhost:5001

pipenv run python blockchain.py
pipenv run python blockchain.py -p 5001

然後在節點 2 上挖兩個塊,確保是更長的鏈,然後在節點 1 上訪問介面 /nodes/resolve,這時節點 1 的鏈會透過共識演演算法被節點 2 的鏈取代。

好啦,你可以邀請朋友們一起來測試你的區塊鏈。


編號378,輸入編號直達本文

●輸入m獲取文章目錄

贊(0)

分享創造快樂