想學習區塊鏈?那就用 Python 構建一個

Daniel Van Flymen發表於2018-07-30
 瞭解區塊鏈是如何工作的最快的方法是構建一個。

你看到這篇文章是因為和我一樣,對加密貨幣的大熱而感到興奮。並且想知道區塊鏈是如何工作的 —— 它們背後的技術基礎是什麼。

但是理解區塊鏈並不容易 —— 至少對我來說是這樣。我徜徉在各種難懂的視訊中,並且因為示例太少而陷入深深的挫敗感中。

我喜歡在實踐中學習。這會使得我在程式碼層面上處理主要問題,從而可以讓我堅持到底。如果你也是這麼做的,在本指南結束的時候,你將擁有一個功能正常的區塊鏈,並且實實在在地理解了它的工作原理。

開始之前 …

記住,區塊鏈是一個 不可更改的、有序的 記錄(被稱為區塊)的鏈。它們可以包括交易transaction、檔案或者任何你希望的真實資料。最重要的是它們是通過使用雜湊連結到一起的。

如果你不知道雜湊是什麼,這裡有解釋

本指南的目標讀者是誰? 你應該能輕鬆地讀、寫一些基本的 Python 程式碼,並能夠理解 HTTP 請求是如何工作的,因為我們討論的區塊鏈將基於 HTTP。

我需要做什麼? 確保安裝了 Python 3.6+(以及 pip),還需要去安裝 Flask 和非常好用的 Requests 庫:

當然,你也需要一個 HTTP 客戶端,像 Postman 或者 cURL。哪個都行。

最終的程式碼在哪裡可以找到? 原始碼在 這裡

第 1 步:構建一個區塊鏈

開啟你喜歡的文字編輯器或者 IDE,我個人喜歡 PyCharm。建立一個名為 blockchain.py 的新檔案。我將僅使用一個檔案,如果你看暈了,可以去參考 原始碼

描述一個區塊鏈

我們將建立一個 Blockchain 類,它的建構函式將去初始化一個空列表(去儲存我們的區塊鏈),以及另一個列表去儲存交易。下面是我們的類規劃:

我們的 Blockchain 類的原型

我們的 Blockchain 類負責管理鏈。它將儲存交易並且有一些為鏈中增加新區塊的輔助性質的方法。現在我們開始去充實一些類的方法。

區塊是什麼樣子的?

每個區塊有一個索引、一個時間戳(Unix 時間)、一個交易的列表、一個證明(後面會詳細解釋)、以及前一個區塊的雜湊。

單個區塊的示例應該是下面的樣子:

我們的區塊鏈中的塊示例

此刻,鏈的概念應該非常明顯 —— 每個新區塊包含它自身的資訊和前一個區域的雜湊。這一點非常重要,因為這就是區塊鏈不可更改的原因:如果攻擊者修改了一個早期的區塊,那麼所有的後續區塊將包含錯誤的雜湊。

這樣做有意義嗎?如果沒有,就讓時間來埋葬它吧 —— 這就是區塊鏈背後的核心思想。

新增交易到一個區塊

我們將需要一種區塊中新增交易的方式。我們的 new_transaction() 就是做這個的,它非常簡單明瞭:

new_transaction() 執行後將在列表中新增一個交易,它返回新增交易後的那個區塊的索引 —— 那個區塊接下來將被挖礦。提交交易的使用者後面會用到這些。

建立新區塊

當我們的 Blockchain 被例項化後,我們需要一個創世區塊(一個沒有祖先的區塊)來播種它。我們也需要去新增一些 “證明” 到創世區塊,它是挖礦(工作量證明 PoW)的成果。我們在後面將討論更多挖礦的內容。

除了在我們的建構函式中建立創世區塊之外,我們還需要寫一些方法,如 new_block()new_transaction() 以及 hash()

上面的內容簡單明瞭 —— 我新增了一些註釋和文件字串,以使程式碼清晰可讀。到此為止,表示我們的區塊鏈基本上要完成了。但是,你肯定想知道新區塊是如何被建立、打造或者挖礦的。

理解工作量證明

工作量證明Proof of Work(PoW)演算法是在區塊鏈上建立或者挖出新區塊的方法。PoW 的目標是去撞出一個能夠解決問題的數字。這個數字必須滿足“找到它很困難但是驗證它很容易”的條件 —— 網路上的任何人都可以計算它。這就是 PoW 背後的核心思想。

我們來看一個非常簡單的示例來幫助你瞭解它。

我們來解決一個問題,一些整數 x 乘以另外一個整數 y 的結果的雜湊值必須以 0 結束。因此,hash(x * y) = ac23dc…0。為簡單起見,我們先把 x = 5 固定下來。在 Python 中的實現如下:

在這裡的答案是 y = 21。因為它產生的雜湊值是以 0 結尾的:

在比特幣中,工作量證明演算法被稱之為 Hashcash。與我們上面的例子沒有太大的差別。這就是礦工們進行競賽以決定誰來建立新塊的演算法。一般來說,其難度取決於在一個字串中所查詢的字元數量。然後礦工會因其做出的求解而得到獎勵的幣——在一個交易當中。

網路上的任何人都可以很容易地去核驗它的答案。

實現基本的 PoW

為我們的區塊鏈來實現一個簡單的演算法。我們的規則與上面的示例類似:

找出一個數字 p,它與前一個區塊的答案進行雜湊運算得到一個雜湊值,這個雜湊值的前四位必須是由 0 組成。

為了調整演算法的難度,我們可以修改前導 0 的數量。但是 4 個零已經足夠難了。你會發現,將前導 0 的數量每增加一,那麼找到正確答案所需要的時間難度將大幅增加。

我們的類基本完成了,現在我們開始去使用 HTTP 請求與它互動。

第 2 步:以 API 方式去訪問我們的區塊鏈

我們將使用 Python Flask 框架。它是個微框架,使用它去做端點到 Python 函式的對映很容易。這樣我們可以使用 HTTP 請求基於 web 來與我們的區塊鏈對話。

我們將建立三個方法:

  • /transactions/new 在一個區塊上建立一個新交易
  • /mine 告訴我們的伺服器去挖礦一個新區塊
  • /chain 返回完整的區塊鏈

配置 Flask

我們的 “伺服器” 將在我們的區塊鏈網路中產生一個單個的節點。我們來建立一些樣板程式碼:

對上面的程式碼,我們做新增一些詳細的解釋:

  • Line 15:例項化我們的節點。更多關於 Flask 的知識讀 這裡
  • Line 18:為我們的節點建立一個隨機的名字。
  • Line 21:例項化我們的區塊鏈類。
  • Line 24–26:建立 /mine 端點,這是一個 GET 請求。
  • Line 28–30:建立 /transactions/new 端點,這是一個 POST 請求,因為我們要傳送資料給它。
  • Line 32–38:建立 /chain 端點,它返回全部區塊鏈。
  • Line 40–41:在 5000 埠上執行伺服器。

交易端點

這就是對一個交易的請求,它是使用者傳送給伺服器的:

因為我們已經有了新增交易到塊中的類方法,剩下的就很容易了。讓我們寫個函式來新增交易:

建立交易的方法

挖礦端點

我們的挖礦端點是見證奇蹟的地方,它實現起來很容易。它要做三件事情:

  1. 計算工作量證明
  2. 因為礦工(我們)新增一個交易而獲得報酬,獎勵礦工(我們) 1 個幣
  3. 通過將它新增到鏈上而打造一個新區塊

注意,挖掘出的區塊的接收方是我們的節點地址。現在,我們所做的大部分工作都只是與我們的 Blockchain 類的方法進行互動的。到目前為止,我們已經做完了,現在開始與我們的區塊鏈去互動。

第 3 步:與我們的區塊鏈去互動

你可以使用簡單的 cURL 或者 Postman 通過網路與我們的 API 去互動。

啟動伺服器:

我們通過生成一個 GET 請求到 http://localhost:5000/mine 去嘗試挖一個區塊:

使用 Postman 去生成一個 GET 請求

我們通過生成一個 POST 請求到 http://localhost:5000/transactions/new 去建立一個區塊,請求資料包含我們的交易結構:

使用 Postman 去生成一個 POST 請求

如果你不使用 Postman,也可以使用 cURL 去生成一個等價的請求:

我重啟動我的伺服器,然後我挖到了兩個區塊,這樣總共有了 3 個區塊。我們通過請求 http://localhost:5000/chain 來檢查整個區塊鏈:

第 4 步:共識

這是很酷的一個地方。我們已經有了一個基本的區塊鏈,它可以接收交易並允許我們去挖掘出新區塊。但是區塊鏈的整個重點在於它是去中心化的decentralized。而如果它們是去中心化的,那我們如何才能確保它們表示在同一個區塊鏈上?這就是共識Consensus問題,如果我們希望在我們的網路上有多於一個的節點執行,那麼我們將必須去實現一個共識演算法。

註冊新節點

在我們能實現一個共識演算法之前,我們需要一個辦法去讓一個節點知道網路上的鄰居節點。我們網路上的每個節點都保留有一個該網路上其它節點的註冊資訊。因此,我們需要更多的端點:

  1. /nodes/register 以 URL 的形式去接受一個新節點列表
  2. /nodes/resolve 去實現我們的共識演算法,由它來解決任何的衝突 —— 確保節點有一個正確的鏈。

我們需要去修改我們的區塊鏈的建構函式,來提供一個註冊節點的方法:

一個新增鄰居節點到我們的網路的方法

注意,我們將使用一個 set() 去儲存節點列表。這是一個非常合算的方式,它將確保新增的節點是冪等idempotent的 —— 這意味著不論你將特定的節點新增多少次,它都是精確地只出現一次。

實現共識演算法

正如前面提到的,當一個節點與另一個節點有不同的鏈時就會產生衝突。為解決衝突,我們制定一個規則,即最長的有效的鏈才是權威的鏈。換句話說就是,網路上最長的鏈就是事實上的區塊鏈。使用這個演算法,可以在我們的網路上節點之間達到共識。

第一個方法 valid_chain() 是負責來檢查鏈是否有效,它通過遍歷區塊鏈上的每個區塊並驗證它們的雜湊和工作量證明來檢查這個區塊鏈是否有效。

resolve_conflicts() 方法用於遍歷所有的鄰居節點,下載它們的鏈並使用上面的方法去驗證它們是否有效。如果找到有效的鏈,確定誰是最長的鏈,然後我們就用最長的鏈來替換我們的當前的鏈。

在我們的 API 上來註冊兩個端點,一個用於新增鄰居節點,另一個用於解決衝突:

這種情況下,如果你願意,可以使用不同的機器來做,然後在你的網路上啟動不同的節點。或者是在同一臺機器上使用不同的埠啟動另一個程式。我是在我的機器上使用了不同的埠啟動了另一個節點,並將它註冊到了當前的節點上。因此,我現在有了兩個節點:http://localhost:5000http://localhost:5001

註冊一個新節點

我接著在節點 2 上挖出一些新區塊,以確保這個鏈是最長的。之後我在節點 1 上以 GET 方式呼叫了 /nodes/resolve,這時,節點 1 上的鏈被共識演算法替換成節點 2 上的鏈了:

工作中的共識演算法

然後將它們封裝起來 … 找一些朋友來幫你一起測試你的區塊鏈。


我希望以上內容能夠鼓舞你去建立一些新的東西。我是加密貨幣的狂熱擁護者,因此我相信區塊鏈將迅速改變我們對經濟、政府和記錄儲存的看法。

 

相關文章