1. 程式人生 > >300行ABAP代碼實現一個最簡單的區塊鏈原型

300行ABAP代碼實現一個最簡單的區塊鏈原型

指向 repo 方法調用 輸入參數 transacti ui控件 挖礦 太多的 work

不知從什麽時候起,區塊鏈在網上一下子就火了。

技術分享圖片

這裏Jerry就不班門弄斧了,網上有太多的區塊鏈介紹文章。我的這篇文章沒有任何高大上的術語,就是300行ABAP代碼,實現一個最簡單的區塊鏈原型。

我個人覺得,同區塊鏈本身的實現技術相比,更難的事情是如何找到一個合適的業務場景,把區塊鏈集成到SAP產品中去,讓它發揮出作用。

這篇文章包含三個版本,每個版本在前一版本基礎上增添了一些新的功能。

版本1:區塊和鏈這兩個數據結構的實現

區塊鏈,顧名思義,由區塊組成的一條鏈。

下圖和我們在大學計算機專業課《數據結構》裏學到的單鏈表很像。在這個版本裏,每個區塊包含了最基本的字段:塊索引,塊的創建時間戳,當前塊的哈希值(hash)和前一塊的哈希值。每個區塊的pHash字段存儲了前一塊的哈希值,這樣就構成了一個鏈表。鏈表的第一個節點,就是下圖最左邊紅色擡頭的區塊為創世塊,其索引為0,pHash字段為空。

技術分享圖片

區塊的ABAP實現:ZCL_BLOCK。上圖所示的字段都建模在這個類裏,出於簡單起見,大部分字段設置為public。

技術分享圖片

每個區塊的哈希值是由該區塊所有其他字段的值作為輸入,通過SHA1算法計算出來,存儲到字段mv_hash裏。

技術分享圖片

鏈的實現:ZCL_BLOCKCHAIN

技術分享圖片

  • ADD_BLOCK: 接受一個區塊的實例作為輸入參數,將該實例的pHash指向當前鏈表尾部的區塊,這樣該實例成為鏈表新的尾部區塊。
  • CONSTRUCTOR: 構造函數,執行鏈的初始化操作,創建創世塊區塊。
  • GET_BLOCK_BY_INDEX: 根據索引訪問指定的區塊。
  • GET_SIZE: 返回鏈裏包含的區塊數量。
  • IS_VALID: 檢查該區塊鏈是否有效。具體檢查邏輯在後續介紹。

第一版的所有代碼在我的github上。

執行測試程序ZBLOCKCHAIN_V1,輸入您想創建的區塊個數,會看到如下輸出:(我選擇的個數是5)

技術分享圖片

因為SAP GUI沒有鏈表的UI控件,因此我用樹控件模擬。

下圖第21行,我把區塊鏈裏索引為1的區塊內容篡改為"Change by Jerry", 然後再執行第23行的is_valid方法進行檢查:

技術分享圖片

因為第21行set_data方法修改了第一個區塊的內容後,會觸發其哈希值的重新計算。這樣第一個區塊的哈希發生了變化(假設從YYY變到了CCC),而第二個區塊的pHash仍然指向第一個區塊變化之前的舊哈希值YYY,因此這個區塊鏈被判定為無效。

技術分享圖片

技術分享圖片

上圖的輸出來自校驗方法is_valid: 遍歷鏈裏每個區塊,比較區塊裏存儲前一區塊哈希值的字段pHash和位於該區塊前一個位置的區塊的哈希值是否一致。

技術分享圖片

版本2:增加挖礦成本,增加對工作量證明(Proof of Work,縮寫為POW)的支持

第二版代碼的地址在我的github上。

這一版的鏈實現類ZCL_BLOCKCHAIN_V2的構造函數增加了一個輸入參數:iv_difficulty。這個參數有什麽用?

仔細觀察第一版測試程序的樹狀輸出,可以看到每個區塊的哈希值沒有任何規律。而第二版的這個輸入參數就是為了提高哈希值的計算成本,即只有當計算出來的哈希值滿足一定規則時,該哈希值才能被區塊鏈所接受。參數iv_difficulty定義了能夠被接受的哈希值的前導零個數。

技術分享圖片

例如我指定前導零個數為3:

技術分享圖片

執行結果:能看到所有的哈希值的前三位都為零。

這裏有兩個問題:

  • 下圖最後一列Nonce的含義是什麽?
  • iv_difficulty這個參數是如何參與哈希值計算的呢?

技術分享圖片

首先在區塊的實現類ZCL_BLOCK裏增加了一個新的成員字段mv_nonce:

技術分享圖片

在將一個區塊實例添加到鏈裏的方法add_block裏,增加了一個方法mine。

技術分享圖片

這個方法裏是一個循環,在循環體內計算出一個哈希值,然後檢查其是否包含指定位數的前導零。如果沒有,將mv_nonce加1,然後繼續循環。mv_nonce也會作為輸入的一部分參與哈希計算。也就是說,最終區塊字段mv_nonce的值代表了代表了在得到符合前導零位數要求的合法哈希值之前,一共經過了多少次計算。而通過在循環裏不斷嘗試最終得到一個合法的哈希值的這一過程,就是區塊鏈圈內俗稱的“挖礦”。

技術分享圖片

在我的測試系統裏,創建10個區塊,前導零個數為4,總共花費了10秒鐘。

技術分享圖片

從這個花費的時間能體會出,POW其實是一種機制,通過引入需要一定工作量的哈希計算來避免區塊鏈被垃圾填充或者區塊內容被篡改。

版本3:使用區塊鏈記錄交易明細,增加挖礦獎勵

這一版的源代碼:

https://github.com/i042416/KnowlegeRepository/tree/master/ABAP/blockchain/v3

使用ZCL_TRANSACTION來代表一筆交易,包含三個字段:mv_from_address(支付方),mv_to_address(收款方)和mv_amount(交易金額)。

技術分享圖片

在這一版本裏,首先被增強的是ZCL_BLOCK3: 去掉了前兩個版本使用的mv_index和mv_data字段,增加了一個字段mt_transaction, 存儲的是交易的集合。

技術分享圖片

在計算哈希值時,交易類的每一個字段也要參與計算:

技術分享圖片

類ZCL_BLOCKCHAIN_V3增加了一個新的成員變量mt_pending_trans。每次調用方法create_transaction,並不會創建一個新的區塊用於記錄該條交易,只是簡單地把該條交易添加到待處理任務隊列mt_pending_trans裏。

技術分享圖片

字段mv_mine_reward存儲了挖礦的獎勵,硬編碼成100。

技術分享圖片

這個待處理任務隊列僅當方法mine_pending_trans被調用時才會得到處理。

第6行的區塊實例的mine方法調用之後,計算出一個符合前導零規範的哈希值。接著待處理任務隊列被清空,然後一個新的交易記錄在第13行被創建出來,作為挖礦的獎勵,獎勵方的賬號信息由輸入參數iv_award_address定義。

技術分享圖片

既然現在交易信息存儲在了每個區塊裏,那麽簡單遍歷這些區塊,就能得出某個賬號最後的余額是多少。采用的邏輯是,遍歷每個區塊記錄的每筆交易,如果某帳號出現在交易記錄的支付方,則余額減去當前這筆交易的交易金額(第5行), 反之賬號如果出現在發送方,則余額加上交易金額(第9行)。

技術分享圖片

測試程序如下。因為Tom轉了100元給Jerry,Jerry又轉了10元給Tom,然後第15行挖礦設置的獎勵賬號是Jerry,故最後Jerry的余額是100-10+100 = 190元。

技術分享圖片

希望您讀完本文之後,對區塊鏈的工作原理有一個最基本的認識。感謝閱讀。

要獲取更多Jerry的原創技術文章,請關註公眾號"汪子熙"或者掃描下面二維碼:

技術分享圖片

技術分享圖片

300行ABAP代碼實現一個最簡單的區塊鏈原型