1. 程式人生 > >用C++來搭建一條迷你區塊鏈!

用C++來搭建一條迷你區塊鏈!

640?wx_fmt=jpeg

程式設計師的天賦技能就是通過程式碼實踐自己的想法,完成一個作品會有相當的成就感,所以今天我們以 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 的核心程式主要包含以下結構:

tinychain
├── blockchain
├── consensus
├── database
├── network
├── http-server
└── node

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

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

cli-tinychain
├── JSON
└── http-client

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

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

基礎元件
├── log
├── JSON-paser
├── sha256
└── key-pair
區塊資料結構設計

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

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

區塊頭的設計

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

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

{
   "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
}
  • target_bits 表示當前區塊的目標值;

  • hash 表示這個區塊的雜湊;

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

  • nonce 表示隨機數;

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

  • previous_block_hash 指向前向區塊雜湊;

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

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

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

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

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

{
    "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"
        }],
}

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

地址設計

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

記憶體池

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

雜湊計算

區塊和交易的雜湊計算均使用 SHA-256。

開發環境搭建

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

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

所以我們的工具鏈是:

  1. gcc 或 clang

  2. cmake

  3. 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 容器中。

  總結  

今天大致介紹了實踐一個迷你區塊鏈的思路,我們先劃定了實踐的範圍,接著考慮了技術選型,然後細化了詳細功能,考慮了一個區塊鏈需要的資料結構,最後考慮了開發環境和測試環境的搭建。(轉自InfoQ)

640?

640?wx_fmt=gif

免責宣告:本文系網路轉載,版權歸原作者所有。如涉及作品版權問題,請與我們聯絡,我們將根據您提供的版權證明材料確認版權並支付稿酬或者刪除內容。