RISC-V 在區塊鏈智慧及雲原生中的應用、機遇與挑戰

NervosNetwork發表於2021-09-28
本文轉載自:斗篷客(ID:wearecloakman)

區塊鏈如同 2000 年前後的網際網路,正一步步走入我們每個人的生活中。

作為區塊鏈整套技術中的核心之一,智慧合約/虛擬機器的設計在推動區塊鏈創新的程式中,正扮演著越來越重要的角色。由此出發,虛擬機器的設計也呈現著百花齊放的姿態。

祕猿科技根據對智慧合約層以及區塊鏈虛擬機器的理解與反思,基於 RISC-V 硬體指令集打造了虛擬機器 CKB-VM。在這次分享中,我們將會介紹我們選擇 RISC-V 打造虛擬機器的緣由,並展示 RISC-V 為我們的區塊鏈落地與創新中帶來的前所未有的靈活性。

迄今為止,CKB-VM 是市面上唯一一個能在智慧合約中直接部署密碼學演算法的區塊鏈虛擬機器,其他任何區塊鏈虛擬機器層都不具備實現達到與 CKB-VM 相近的能力。

與此同時,我們認為 CKB-VM 並不僅僅在區塊鏈領域中適用, 在晶片逐漸碎片化的今天,CKB-VM 可以為雲應用開發者提供一個穩定的指令集,並通過底層優化實現,將程式碼執行在更多體系結構之上,實現真正意義上的 write once, run anywhere 的願景。
 

分享提綱:

1、探討區塊鏈對智慧合約的需求,以及現有設計中遇到的問題

2、介紹 CKB-VM 的核心設計,以及我們如何通過引入 RISC-V 指令集,來解 CKB-VM 遇到的實際問題

3、探討 CKB-VM 在區塊鏈之外的雲原生領域中同樣廣闊的應用

4、回顧在 CKB-VM 實現過程中遇到的挑戰與應對方式,展示未來的工作計劃
 

現有區塊鏈虛擬機器困境以及如何使用 RISC-V 解決

 

在一個通用計算機平臺上模擬另一個計算機具有悠久的歷史,我們通常將這種位於源 ISA 和目標 ISA 之間的翻譯軟體稱為虛擬機器。

自從以太坊開始,在區塊鏈系統中加入的智慧合約代表了區塊鏈從一個單一的公共賬本到金融服務應用角色轉變的一個重要階段。

為了支撐智慧合約中的業務,我們希望底層虛擬機器足夠安全,這不僅僅意味著虛擬機器本身的安全性, 還意味著虛擬機器位元組碼本身是易審計和易靜態分析的,同時除了安全性之外,效能也是一個重要的考慮目標,這要求從源 ISA 到目標 ISA 之間的翻譯可以使用盡量少的程式碼完成。

除了安全性和效能之外,我們還希望這個虛擬機器的周邊生態是完善的,有多種不同的高階語言,多種不同的 IDE 和數量眾多的開發者輔助工具幫助應用開發者編寫出健壯的程式碼。

目前最廣泛流行在世界上的區塊鏈虛擬機器只有兩類: EVMWebAssembly

EVM 它存在諸多的問題, 例如它的動態跳轉(dynamic jumps)機制導致 EVM 程式碼無法被靜態分析,導致 EVM 上安全性漏洞頻發,而它的 256 位整數又導致虛擬機器效能極差,這還不是最糟糕的,最糟糕的問題是:由於區塊鏈的性質我們無法對 EVM 做任何根本上的升級。

在 EVM 負重前行之時,一些人轉而使用 WebAssembly,但是很難說 WebAssembly 是一個好的選擇。

WebAssembly,顧名思義是為了 Web 而發明的,它被設計為執行在瀏覽器之中,但目前的現實是,區塊鏈虛擬機器對效能的要求已經超過一個瀏覽器客戶端對效能的要求。

同時 WebAssembly 的位元組碼是一種 AST(抽象語法樹)位元組碼而非傳統意義上的指令, 前者是一種樹形結構,而後者則是一種一維的指令流。這種區別使得在載入 WebAssembly 的過程中需要消耗許多時間用於解析二進位制資料到 AST。但在區塊鏈的世界中,由於大多數應用的計算量都不高,也不會像瀏覽器那樣長時間執行,因此程式碼的載入速度和其執行速度同等重要。

如果我們可以在區塊鏈虛擬機器上採用 RISV-V 指令集,不但可以有效解決以上的問題,還有許多額外的好處:

  • RISC-V 是一個簡單的硬體指令集, 經過良好的設計和廣泛的測試,不會存在像 EVM 那樣過多的設計失誤;
  • RISC-V 工作在更低的層次(相比 EVM 和 WebAssembly)。目前世界的一個趨勢是「簡單的硬體, 複雜的軟體」,我們可以在如路由器、智慧家居等很多領域發現這一點,區塊鏈也應該如此;
  • RISC-V 程式採用 ELF 封裝, 其裝載速度更快;
  • RISC-V 程式可以很容易做 JIT 和 AOT 編譯,其效能上限更高;
  • RISC-V 擁有完善的工具鏈, 可以很方便的對程式進行分析和除錯,對於廣泛的開發者群體來說這至關重要;
  • 最後, 率先在區塊鏈虛擬機器上使用 RISC-V,對未來在 RISC-V 硬體上部署區塊鏈節點有強大的推動作用。
     

CKB-VM 的核心設計邏輯

 
在一個資源受限的環境下,如何對資源進行有效的管理和使用是構建一個高效能平臺的關鍵,區塊鏈上的所有使用者在有限的時間裡共享有限的資源,這使得區塊鏈應用程式在其分配的資源量內高效執行變得更加重要。

這裡有兩個關鍵的點,其一是高效執行,其二是限制資源的使用量 。基於此,CKB-VM的核心設計邏輯:

高效執行

CKB-VM 決定引入 RISC-V 指令集的關鍵點之一是,絕大多數 RISC-V IMC 指令集內的指令都可以語義上等價幾條 x64 指令的組合,這意味著我們只需要極少的額外消耗就能在 x64 平臺上構建一個 RISC-V 虛擬機器。

例如 RISC-V 中的 AND 指令與 BGE 指令都能在 x64 平臺下以極其精簡的方式實現,這裡摘取 CKB-VM 的 ASM 直譯器實現 AND 指令的程式碼,你可以看到我們使用了一些巨集,這讓整個流程變得異常清晰:

資源限制

 
CKB-VM 允許開發人員自定義一個函式,該函式接收一個 RISC-V 指令並返回該指令的 cycles 消耗,CKB-VM 內建的檢查程式會統計已執行的 RISC-V cycles 數並在預設閾值處停止程式執行。

這將保證執行在 CKB-VM 中的所有應用都能在有限的時間內停止,它避免了在區塊鏈下出現最糟糕的後果: 一個惡意應用停止了區塊鏈的執行。CKB-VM 在內部使用一個位元組陣列模擬 RISC-V 的記憶體,我們限制應用程式執行時所需要的記憶體總量。

在許多時候我們會遇到取捨問題,更大的記憶體代表著更高的靈活性,但是正如俗話所說任何硬幣都有兩面,大記憶體也會導致在初始化虛擬機器時消耗較多的時間。CKB-VM 在靈活性與初始化速度之間找到了一個微妙的平衡,這個平衡的記憶體限制是四兆。我們做了許多測試,表明在四兆的記憶體限制下能實現絕大多數密碼學演算法和足夠複雜的業務邏輯。
 

記憶體的延遲

 
初始化在中文編碼界一直流傳著一些意義不明的暗號,例如「燙燙燙」和「屯屯屯」。它們常常出現在 VS 的 debug 模式中,當你看到這些符號就意味著你的程式訪問了未初始化的記憶體。這涉及到一個底層設計,那就是 x64 程式向作業系統申請到的記憶體是未初始化的。

但對於 RISC-V 情況則有些不同,因為其規範中規定了記憶體必須以零值進行初始化。由於 malloc 和 calloc 之間存在著巨大的效能差距,使用 calloc 的方式去申請四兆的記憶體顯得十分浪費, CKB-VM 所作的決定是延遲記憶體的初始化,即只有使用到相關記憶體頁時,該記憶體頁才會被零值初始化,未被使用的記憶體頁被保持在未初始化狀態。這有效的提升了執行一些使用記憶體很少的程式的執行效率。
 

W^X 記憶體保護策略

 
CKB-VM 同樣被設計在區塊鏈中執行不受信任的應用程式程式碼,這些程式碼可能來自粗心的開發者,也有可能來自蓄意的攻擊者。一種常見但有效的攻擊方式是攻擊者構造特定的輸入資料,使得程式將 CPU 指令寫入用於儲存資料的記憶體空間,然後執行這些指令。

CKB-VM 做了內建的 W^X(write xor execute)保護。 它是一種記憶體保護策略,應用程式的地址空間中的每個頁都可以是可寫的或可執行的, 但不能同時是可寫的或可執行的。這種機制允許更靈活地編寫應用程式,而不會過於擔心一些程式設計錯誤導致意外的執行了攻擊者的程式碼。
 
CKB-VM 通過許多底層邏輯的優化,使它成為了兼顧安全性和效能的運算平臺。
 

CKB-VM 在區塊鏈之外雲原生領域的廣泛應用

 

RISC-V + CKB-VM + Cloud Native RISC-V 是一項全新的技術,它正在硬體領域中快速發展,但我認為它的潛力遠不止如此——在未來它可能將在雲原生領域扮演更重要的角色。

目前雲領域基本上是 x86 和 AMD 的市場,我認為 RISC-V 可以以一種巧妙的方式加入。它不同於直接競爭,而是藉助 CKB-VM,將 RISC-V 程式通過 CKB-VM 執行在 x86 平臺上。 在有足夠的市場之後,再嘗試使用真實的 RISC-V 硬體,同時對於雲廠商,他們不需要去承擔切換架構的風險投入, 因此推進的阻力會比直接上 RISC-V 硬體小很多。

我們來看一下 RISC-V + CKB-VM + Cloud Native 相比傳統的 Cloud Native 有什麼優點。

目前 Cloud Native 的一個普遍的實踐是首先啟動一個 Docker 做環境和資源的隔離,然後在 Docker 內部直接執行一個二進位制程式,或者間接的通過 NodeJS,Python 或是一個 JVM 來執行使用者的指令碼程式碼。

我不禁要問為什麼要將事情搞得如此複雜呢?

特別是隨著像 Rust,Golang 等高階語言的 RISC-V 後端逐漸成熟,我們完全可以將 RISC-V 看作一種「跨平臺的位元組碼」,通過 CKB-VM,它可以真正實現 write once, run anywhere 的願景。
 

較 Docker 而言更細緻的許可權控制

 

相較於 Docker 提供的資源隔離的基本功能,採用 RISC-V + CKB-VM 的方式可以提供更多雲端計算平臺所需要的更加細粒度的許可權控制。 RISC-V 程式採用系統呼叫(system call)的方式與作業系統通訊,CKB-VM 的實現方式上可以代理應用程式的發出的全部系統呼叫,之後,雲端計算平臺便可以根據使用者的相關許可權與當前的資源使用情況等來決定是否響應這次系統呼叫。例如:

  • 控制應用程式使用的最大檔案控制程式碼數量
  • 控制應用程式建立的 TCP 連結數量
  • 控制應用程式的 IO 使用量

以上這些細粒度的控制很難使用 Docker 實現,至少不會很直觀,而採用 RISC-V + CKB-VM 的話,由於資源的請求與釋放全部通過系統呼叫,雲端計算平臺可以在應用程式的資源和許可權上做到幾乎無限粒度的控制
 

代理資源請求

 
當我們在程式碼中使用 open 函式的時候,我們在幹什麼?

我們是真的「開啟」了硬碟上的一個真實存在的檔案嗎?

並不是,我們是像作業系統發出了一個請求,作業系統有時會從硬碟上讀取這個檔案,有時則是從快取中給我們返回了這個檔案,這取決於作業系統的想法。這種情況下可以說作業系統管理了硬碟資源。

雲原生程式設計應當與本機程式設計具有相似的程式設計體驗, 但目前這方面行業內做的遠遠不夠。許多雲廠商都有提供雲端儲存的功能, 但是在大多數情況下,仍需要先行安裝這些廠商的 SDK,然後通過相關 API 進行訪問。這種體驗造成了一種雲應用與原生應用的割裂感覺。

正如上面所說的,CKB-VM 代理了應用程式發出的全部系統呼叫,在採用 RISC-V + CKB-VM 的方案時,可以做到雲程式碼與原生程式碼完全一致:

例如當開發者寫下 open("/foo/bar") 時,如果其程式碼是在本機執行,那麼程式會開啟本機檔案系統下的 /foo/bar 檔案;而如果將此程式碼執行在雲端計算平臺上,它將會開啟你當前賬號下相關雲端儲存桶裡的 /foo/bar 檔案。

最重要的一點是這一切都是自動的,無需開發者對程式碼做任何修改, 甚至不需要重新編譯!
 

毫秒級的冷啟動,極低的資源消耗,極快的執行速度

 
CKB-VM 的這些特性,使得它非常適合作為 Lambda 函式執行。這一切特性使之全面優於傳統的基於 Docker 的 Lambda 函式。並且可以為雲廠商節省一大筆機器費用
 

多語言支援

 
僅僅就目前而言, C/C++, Golang 和 Rust 等通用語言均可以生成較為優質的 RISC-V 程式碼,它們可以直接執行在 CKB-VM 中。更進一步的話,甚至可以通過將 JavaScript,Lua 和 Ruby 等直譯器編譯為 RISC-V ,而支援在 CKB-VM 。
 

CKB-VM 的過去與未來

 

祕猿科技在 CKB-VM 開發過程中,遇到過許多富有挑戰性的難題,在解決這些問題的過程中積累了大量的開發經驗。
 

如何高效的對指令集進行模擬?

 
CKB-VM 最初使用 Rust 實現了一個直譯器,但是 Rust 直譯器編譯後的程式碼質量一般,其效能遠不如手工編寫的彙編程式碼。CKB-VM 做了另外兩個嘗試, 其一是 AOT 編譯器,它會在執行前首先將 RISC-V 程式編譯為 x64 程式。其二是手工編寫的 ASM 直譯器,相比起 AOT 編譯器,這可以在暫存器分配層面上做到更加細緻的控制。

例如執行環境的上下文資訊,或是指令中的源運算元,目的運算元和立即數,這些資料在整個執行階段都被固定分配在某幾個暫存器中。這一切都工作的非常好,但在嘗試將 B extension 加入 CKB-VM 的時候遇到了一點問題,例如,B extension 中的 bfp 指令的實現對於手工編寫的 ASM 程式碼來說就過於複雜了(無論對於邏輯的實現還是暫存器分配來說):

為此,在 ASM 的直譯器迴圈中,當指令被解碼之後,將根據指令的型別決定指令的執行路徑: 在 CKB-VM 中成為快速路徑慢速路徑。在快速路徑下,指令的執行過程將在彙編程式碼內部處理完畢;在慢速路徑下,指令的執行會被交由 Rust 直譯器來做。

考慮到這些複雜指令出現在程式中的頻次很低,同時可以在應用程式程式碼中有意的避免使用這種複雜指令,因此快速路徑+慢速路徑結合使用的方式幾乎可以在不影響主要效能指標的前提下避免在彙編實現的直譯器迴圈中新增過重的計算過程。
 

如何對虛擬機器進行測試?

 
首先,保證虛擬機器實現能通過官方的測試集,這能提供最低限度的正確性保障。

其次,使用模糊測試(fuzzing test)可以對程式碼進行更好的覆蓋,利用程式生成大量隨機的有意義或無意義的程式碼,分別使用 CKB-VM 與其它現有的 RISC-V 模擬器例如 riscvOVPsim, spike 等執行並比對它們的最終結果。
 

CKB-VM 的未來開發計劃

 
我們計劃為 CKB-VM 提供 V extension(向量指令)的支援,這將允許我們以一種新的思路去進一步優化區塊鏈中的密碼學演算法。

演算法的向量化是一個有趣的挑戰,絕大多數演算法或多或少都是可以在一定程度上並行的,換句話說,我們總能找到一種可以用向量化思想表示的演算法來完全或部分解決問題。對於 CKB-VM 來說,向量指令的底層可以通過 SIMD 或者多執行緒來解決。

相關文章