06.20 如何搭建一條迷你區塊鏈?

如何搭建一條迷你區塊鏈?

作者 | 陳浩

出處 | 《深入淺出區塊鏈》專欄

程序員的天賦技能就是通過代碼實踐自己的想法,完成一個作品會有相當的成就感。

今天我們終於也來到了實踐環節。我將以 C++14 的代碼為例,和你分享設計並實現一個迷你區塊鏈的例子。

目標和範圍

首先我們要知道達成的目標,根據目標劃定工作範圍。

考慮到我們無法搭建一個類似比特幣的龐大 P2P 網絡,也沒有太多精力實現一個真正意義上的完整功能的全節點錢包,而且完整的全節點過於複雜,會讓學習者迷失在細節中。

所以我們的目標是:構建一個包含僅有基礎功能的全節點客戶端,它可能沒有太炫酷的 UI 頁面,也沒有複雜的命令,它們可以提供下面的功能。

  1. 提供 P2P 節點發現和同步區塊的功能;

  2. 提供創建公私鑰對的功能;

  3. 提供發送交易的功能;

  4. 提供交易查詢的功能;

  5. 提供餘額查詢的功能;

  6. 提供挖礦的功能,在任意地址上都可以發起單機挖礦;

  7. 提供基礎日誌,方便跟蹤監視。

以上 7 個功能基本涵蓋了一個區塊鏈全節點的主要功能,但是,由於我的時間有限,代碼不能全部實現,主要是講解設計和實現思路。後續我會逐漸完善代碼,你也可以一起參與。

代碼開源在:https://github.com/betachen/tinychain

技術選型

我們在深入區塊鏈技術專題中說到過,區塊鏈的四個核心技術概念:P2P 網絡、賬戶模型與存儲、共識、加密模塊。

首先,P2P 網絡模塊是區塊鏈的最底層模塊之一,我們主要考慮方便實現和測試,可選的方案有輕量級消息隊列和 WebSocket。考慮到集成的便利性,我們首選 WebSocket,因為至少需要一個 HTTP JSON-RPC Server,我們可以複用 Server 中的 Websocket 服務。

除了通訊協議之外,還要考慮數據交換格式,我們考慮採用易讀通用的 JSON 格式,而不是像比特幣一樣的數據序列化格式,後期更改可以考慮升級到 Protobuf,後者優勢主要體現在性能上。而在我們的例子中,性能永遠不是首先考慮的,更多是它的易讀和易調試性。

其次,我們來說說賬戶加密部分,由於 ECDSA 非對稱加密模塊過於複雜,我們選用 OpenSSL 庫中的 RSA 算法作為加密模塊。而交易模型上,我們考慮使用 UTXO 模型,因為狀態模型需要維護狀態,可能會帶來額外的代碼複雜度。

再來說說數據庫存儲,這個模塊需要考慮到易用性和易讀性,我們選用 SQLite 3 作為持久化存儲。

最後我來談談共識算法這一模塊,我們選用 PoW 作為共識算法,這是考慮到 PoW 實現起來十分簡單,而且交易和區塊的哈希計算會涉及 SHA-256,使用 PoW 算法我們就可以複用 SHA-256 的代碼,使用 SHA-256 算法作為挖礦算法會降低我們的工作量。

詳細功能

有了技術選型之後,我們再對目標功能點進行細分拆解。

  1. P2P 網絡:節點發現、節點維護、持久化保存、區塊同步。

  2. 公私鑰對:命令行,創建公私鑰對並生成地址,提供私鑰存儲,公私鑰驗證。

  3. 發送交易:命令行,發送成功驗證,輸入是交易哈希。

  4. 交易查詢:命令行,JSON 格式的交易查詢返回,輸入是某個地址。

  5. 餘額查詢:命令行,JSON 格式的餘額查詢返回,輸入是某個地址。

  6. 挖礦:命令行、JSON 格式挖礦信息返回,輸入是某個地址。

  7. 區塊共識:編織區塊鏈的算法,包含創世區塊以及調整全網挖礦難度。

  8. 交易共識:驗證單個交易的算法,包含簽名驗證和 UTXO 驗證。

  9. 基礎日誌:用於監控網絡,區塊驗證等操作。

  10. 區塊持久化存儲:分叉與合併時的一致性,併為查詢提供接口。

  11. 提供格式化輸出交易的功能,這裡的格式化主要指 JSON 格式。

  12. 有效防止雙花交易。

通過詳細的功能拆分我們可以發現,功能點多達三十餘個,如何設計實現這三十多個功能點是我們接下來首先要解決的問題。問題是這三十多個功能點不是孤立的,而是有相互聯繫的,我們先從頂層開始設計。

最頂層是一個區塊節點,一個完整的可執行程序,我們命名為 Tinychain,而對應的命令行客戶端為 cli-tinychain。

Tinychain 的核心程序主要包含以下結構:

<code>tinychain ├── blockchain ├── consensus ├── database ├── network ├── http-server └── node/<code>

我們以 node 為最頂層,那麼 node 會包含其他五個模塊,node 啟動就會把其他 5 個服務啟動。

cli-tinychain 主要包含以下結構:

<code> cli-tinychain ├── JSON └── http-client/<code>

命令行就簡單多了,我們把命令行的執行和計算全部都扔到 tinychian 當中,命令行只用一個 http-client 用 JSON 把 API 包起來即可。

通過分析我們知道,以下組件是必不可少的,但是我們不必自己開發,可以直接選取一些現成的開發包直接集成即可。

<code>基礎組件 ├── log ├── JSON-paser ├── sha256 └── key-pair/<code>

區塊數據結構設計

有了大致的頂層設計已經分類好,那麼接下來我們考慮為每個模塊填充一些數據結構。一個區塊鏈最重要的是區塊,所以我們從區塊開始。

一個區塊包含兩部分,分別是區塊頭和區塊體,區塊頭是一個區塊的元數據,區塊體就是包含交易的列表,所以我們直接設計交易體。

區塊頭的設計

我們參照比特幣的設計,區塊頭包含了前向區塊哈希、默克爾根哈希、時間戳、難度目標、Nonce 值和版本號。

所以我們的結構可能是這樣的。

<code> { "target_bits" : "4575460831240", "hash" : "4a9169e2f4f8673ac9627be0fa0f9e15a9e3b1bc5cd697d96954d25acacd92df", "merkle_tree_hash" : "3d228afc50bc52491f5dd8aa8c416da0d9a16bf829790ea0b7635e5b4d44ab4f", "nonce" : "3852714822920177480", "height" : 1234567, "previous_block_hash" : "4d2544e044bfd2f342220a711b10842bb6cfae551b1bc1ed6152ff5c7f3ff654", "time_stamp" : 1528070857, "transaction_count" : 1, "version" : 1 }/<code>
  • target_bits 表示當前區塊的目標值;

  • hash 表示這個區塊的哈希;

  • merkle_tree_hash 表示這個區塊當中交易列表的默克爾根;

  • nonce 表示隨機數;

  • height 表示當前區塊的高度;

  • previous_block_hash 指向前向區塊哈希;

  • time_stamp 表示生產這個區塊時的時間戳;

  • transaction_count 表示這個區塊當中包含多少筆交易;

  • version 表示區塊的版本號,不代表交易的版本號。

在這裡,我們的區塊頭大小不是固定的,因為它沒有經過序列化,完全以 JSON 表示,所以我們這裡就不考慮字節印第安序的問題了,也不考慮固定長度的問題。

有了區塊頭,我們再看看交易體的設計,由於使用 UTXO 作為交易模型,那麼我們先考慮一個輸入、一個輸出的結構。

<code>{ "hash": "8c14f0db3df150123e6f3dbbf30f8b955a8249b62ac1d1ff16284aefa3d06d87", "version": 1, "input_size": 1, "output_size": 1, "size": 135, "inputs": [{ "prev_out": { "hash": "0000000000000000000000000000000000000000000000000000000000000000", "index":0 iq }, }], "out": [{ "value": "5000000000", "address": "f3e6066078e815bb2" }], }/<code>

我們可以按照這種結構來設計交易體。

地址設計

區塊鏈地址都有通常意義上的地址,我們這裡將公鑰直接算作地址,不再將公鑰進行哈希轉換。

內存池

內存池是指緩存交易的一塊交易緩衝區,這裡一個節點的主要處理對象,所以對內存池的管理,是編織區塊鏈的最重要一步。我們這裡的內存池使用標準庫 STL 中的容器。

哈希計算

區塊和交易的哈希計算均使用 SHA-256。

開發環境搭建

由於選取了 C++ 作為實現方式,搭建工程的過程會比較複雜一點。我們用的是 Ubuntu 16.04 開發環境,默認的 gcc 編譯器是 gcc-5.4,是支持 C++14 標準的。代碼也是全平臺可移植的,如果你使用 Mac,也可以嘗試搭建。

除了 gcc 之外,我們還需要 Cmake 來構建工程。我們也許需要 Boost 庫的支持,例如 Filesystem 和 Datetime 等基礎組件。

所以我們的工具鏈是:

  • gcc 或 clang

  • cmake

  • boost 1.56+ (datetime)

最後我們還需要一個簡單好用的輕量級 Httpserver,我選取了元界代碼中的 Mongoose 庫,這裡的 Mongoose 不是 MongoDB,是由 Cesanta 開源的一個 HTTP Server 庫,支持 epoll 和 select 兩種網絡併發機制,也支持 WebSocket。

當然除了 C++ 實現之外,我們也可以使用 Python 來實現,實際上也有不少 Python 實現的 Demo,但我發現用 Python 實現的例子很多是在單進程中模擬區塊鏈的數據結構,並不是真正意義上的分佈式節點,所以我採取了使用 C++ 實現的策略。

測試環境搭建

我們知道區塊鏈是一個分佈式網絡環境,在開始之前,我們需要構造一個簡單且容易測試的分佈式網絡環境。

我們不可能購買大量的雲計算資源,所以我們推薦你購買一個基礎版的 ECS 節點,2Core 4G 就可以,性能稍好更好,接著我們選用 Docker 來搭建容器集群,在容器中部署節點,其中宿主機作為編譯環境,將編譯完成的錢包部署到全部的 Docker 容器中。

文章出自極客時間專欄《深入淺出區塊鏈》,訂閱專欄可享代碼。

現在訂閱,享雙重福利:

  • 立享¥58,6月 22日會漲價至¥68

  • 訂閱成功後,每邀請一位好友訂閱,你可獲得 12 元返現,好友可獲得 6元返現,多邀多得,上不封頂,立即提現。

訂閱方法:識別下圖二維碼,微信支付,立即成功訂閱。


分享到:


相關文章: