6 月 18 日,由 Facebook 發起的加密數字貨幣項目 Libra 正式亮相。Libra 的白皮書與多個技術文檔也同步發佈,其中着重介紹了新的區塊鏈編程語言 Move 和共識協議 LibraBFT。本文從技術角度對 Move 語言進行解讀,帶領大家對這門語言做一個初步的瞭解。

原文標題:《Libra 區塊鏈編程語言解讀》
作者:王加楠

十分鐘速覽 Libra 區塊鏈編程語言

數字資產管理

Move 最吸引眼球的特性是它提出的一套完整的面向數字資產的編程體系。與現有的區塊鏈編程語言相比,Move 着重強化了數字資產的地位。使用 Move 語言,開發者能夠更靈活、安全地在鏈上定義和管理數字資產。

面臨的挑戰

在區塊鏈上定義數字資產具有挑戰性。因爲物理世界中資產的一些特性,難以在數字世界進行表達。

  • 稀缺性:資產的供應應當受到管控。資產不能被複制,創建新的資產是特權行爲。
  • 訪問控制:系統要能夠確保參與者自身的資產受到訪問控制策略的保護。
現有解決方案

現有的區塊鏈編程語言都具備在區塊鏈上定義數字資產的能力,但仍具有一些不足和侷限。下面以比特幣和以太坊爲例,作一個簡要的說明。

比特幣通過 UTXO 的方式對其資產進行編碼,並使用比特幣腳本來定義資產的轉移規則,確保稀缺性和訪問控制。開發者可以使用比特幣腳本來定義各種不同的訪問控制策略。

然而,比特幣腳本具有相當的侷限性,擴展性很差。比特幣腳本不是圖靈完備的語言,開發者也不能在其中自定義數據類型和過程等。因此,想要在比特幣區塊鏈上定義一種新的數字資產,或者實現更復雜的訪問控制策略,只能藉助一些外部的手段,對開發者不友好。

以太坊使用數值代表以太幣,並在系統層面保證了以太幣的稀缺性和訪問控制。開發者可以使用 EVM 字節碼來編寫智能合約,與鏈上的數字資產進行交互。EVM 字節碼是一種圖靈完備的語言,具有很強的擴展性。開發者不僅可以使用它操作以太幣,也可以自定義數字資產,編寫複雜的訪問控制策略。此外,也可以使用更高級的 Solidity 語言來編寫智能合約,Solidity 代碼在運行之前會被編譯爲 EVM 字節碼。

然而,在以太坊中,自定義的數字資產的級別要低於以太幣。以太幣的安全性有系統級別的保護,而自定義數字資產的安全性只能由開發者來保障。無論是底層的 EVM 字節碼還是高級的 Solidity,開發者都只能用數值來間接代表數字資產。由於在編程語言的層面沒有對稀缺性和訪問控制提供支持,開發者在編碼過程中很容易發生錯誤,從而導致嚴重的後果,比如資產的複製、重用和丟失等。

一等資源

針對上述問題,Move 提出了一等資源的概念。開發者不僅可以利用一等資源來實現安全的數字資產,也可爲數字資產編寫正確的業務邏輯。

Move 將數字資產與其他數據類型(如整數、布爾等)區分開來,將其定義爲資源類型。資源類型的語義受到了線性邏輯的啓發:一個資源不能被複制,也不能被隱式的丟棄(即在程序結束時,資源處於無所屬的狀態),只能進行轉移。除了上述區別以外,資源類型在其他方面與普通的數據類型一樣,都可以保存在數據結構中,可以作爲過程的參數等。

Move 使用模塊對資源進行管理。首先,資源的類型結構定義在模塊中,每個資源都必然定義在某個模塊中。其次,模塊中還定義了能夠操作該資源的過程,例如創建、修改、銷燬等。開發者可以且只能通過模塊提供的公開過程來對資源進行操作,不能自行編碼修改資源。也就是說,當使用資源來定義一種數字資產時,其所有的業務邏輯,包括訪問控制策略,均已經在模塊中定義。而任何脫離、繞過該模塊對數字資產進行的操作都是非法的、不被允許的。

在 Libra 區塊鏈中,Libra 代幣也被定義爲一個資源類型,與用戶自定義的資源一致。這意味着自定義數字資產與 Libra 代幣都受到了同樣的保護。

不過,需要注意的是,Move 語言對數字資產提供的安全性約束僅限於模塊之外,模塊內部的安全性約束仍然需要開發者自行保證。

靈活性

Move 語言的另一個特點是其爲 Libra 提供了更高的靈活性,主要包含以下兩個方面。爲便於理解,下文使用以太坊及 Solidity 語言作比較。

交易腳本

以太坊的交易中,包含了目標智能合約的地址,以及提供給目標智能合約的輸入數據。如果使用 Solidity 語言,則該輸入數據的內容爲函數簽名以及參數列表。即,以太坊中的一筆交易,實質上是調用了一個合約的一個接口(不考慮合約間的調用)。

在 Libra 的交易中,取而代之的是交易腳本。交易腳本是一段完整的、任意內容的 Move 程序。在交易腳本中,可以調用已經發布在賬本狀態中的模塊的過程若干次,並基於調用的結果做一些額外的本地處理,例如簡單的控制流等。

交易腳本爲 Libra 提供了極大的靈活性。用戶不僅可以像以太坊那樣,調用某個模塊的中的過程,還可以很輕易地執行一些一次性的行爲。例如,一次性將某個代幣轉賬給多個人,即使該代幣的模塊沒有提供批量轉賬的過程。

模塊系統

以太坊和 Libra 均使用了基於賬戶的賬本狀態。

在以太坊中,賬戶分爲用戶賬戶和合約賬戶。用戶賬戶不包含數據(以太幣除外)。合約賬戶中,既包含了代碼,又包含了數據。從面向對象的角度來看,以太坊的智能合約就像是單例對象。由於數據無法直接保存在用戶賬戶中,以太坊上的大多數數字資產,都實現了一個單獨的 ERC20 Token 合約,在其中包含了所有用戶的資產信息。

Libra 中可以利用 Move 模塊實現類似智能合約的功能。但 Move 模塊與以太坊中的智能合約在設計上並不相同,要更爲靈活。Move 模塊僅包含代碼(包括資源結構定義和過程),而數據保存在資源中。雖然對資源的操作需要通過模塊中的過程來進行,但二者並沒有捆綁在一起。同一類型的資源可以有若干個,並且發佈在不同的賬戶下。因此,在 Libra 中,數字資產保存在用戶自己的賬戶下,而不是像以太坊一樣集中保存。此外,可以在一個 Move 模塊中定義多種資源類型結構。

雖然 Move 中模塊、資源、過程的關係與面向對象編程中類、對象、方法的關係有些相像,但實際上有很大的區別。Move 模塊的設計更像是函數式編程的風格。

安全性

Move 在設計時充分考慮了安全性,不符合安全性要求的 Move 程序將會被拒之門外。

類型化的字節碼

Move 必須拒絕不滿足關鍵安全屬性(如資源安全性、類型安全性、內存安全性等)的程序,因此需要在程序執行之前進行鏈上驗證。爲此,Move 採用了類型化的字節碼作爲可執行程序的格式。

類型化的字節碼介於高級語言和彙編語言之間。以以太坊爲例,Solidity 是一種高級語言,而 EVM 字節碼可以認爲是彙編語言。Solidity 中雖然也做了各種各樣的驗證,能夠保證編譯出的 EVM 字節碼具有一定的安全性。然而,由於 EVM 在執行時所使用的是 EVM 字節碼,而 Solidity 對安全性的保證實際上發生在編譯過程中,若要進行鏈上驗證,則必須把編譯過程放到鏈上。否則,攻擊者完全可以繞過 Solidity,直接編寫具有漏洞的 EVM 字節碼。一方面,編譯過程在鏈上進行勢必會對性能造成影響。另一方面,EVM 字節碼缺乏數據類型信息,難以進行驗證。綜上所述,採用類型化的字節碼,既提供了安全保證,也避免了編譯帶來的性能損耗。

更利於靜態驗證的設計

出於計算成本的考慮,Move 的鏈上驗證僅包含了部分關鍵的安全屬性。除了鏈上驗證,Move 也被設計爲支持高級的鏈下靜態驗證。爲此,Move 做了以下設計,使其比大多數通用語言更適合靜態驗證。

首先,Move 不支持動態調度。所有調用的目標都能夠被靜態確定。這使得 Move 不必構建複雜的調用圖,就能精確地推斷出程序的執行效果。

其次,有限的可變性。Move 借鑑了 Rust 的「借用檢查」機制,確保每個值在同一時刻最多有一個可變引用。

第三,模塊性。Move 模塊對資源進行了強制的數據抽象和關鍵操作本地化。也就是說,對於模塊以外的程序來說,每個資源都是一個黑盒。外部代碼無法得知資源內部的細節,只能通過模塊的公開過程對資源進行操作。

虛擬機

字節碼解釋器

Move 字節碼解釋器是基於堆棧的,與 CLR 和 JVM 類似。指令使用堆棧中的操作數,並在執行後將結果推入堆棧。

Move 支持六大類字節碼指令:

  • 用於將數據從局部變量複製、移動到堆棧的操作,如 CopyLoc、MoveLoc 等,以及用於將數據從堆棧移動到局部變量的指令,如 StoreLoc。
  • 對類型化堆棧值的操作,例如將常量推入堆棧,以及對堆棧操作數進行算術、邏輯運算。
  • 模塊相關的內建指令,例如:Pack 和 Unpack,用於創建、銷燬模塊的聲明類型;MoveToSender、MoveFrom,用於在帳戶下發布、取消發佈模塊的類型;以及 BorrowField,用於獲取對模塊中某個類型的字段的引用。
  • 引用相關的指令,例如:ReadRef 用於讀引用,WriteRef 用於寫引用,ReleaseRef 用於釋放引用,FreezeRef 用於將可變引用轉換爲不可變引用。
  • 控制流操作,例如條件分支,以及過程調用和返回。
  • 區塊鏈特定的內建操作,例如獲取交易腳本的發送者地址、創建新帳戶等。

除此之外,Move 也提供了密碼學原語,比如 sha3。這些原語由標準庫的模塊實現,而不是字節碼指令。

字節碼驗證器

字節碼驗證器爲模塊和交易腳本強制執行安全屬性。如果不通過字節碼驗證程序,則無法發佈模塊或執行交易腳本。

字節碼驗證器主要進行三個方面的檢查。

  • 結構檢查,確保字節碼錶的格式正確。通過結構檢查,可以發現諸如非法表索引、重複表條目、非法類型簽名等錯誤。
  • 語義檢查,用於發現非法參數、危險引用、資源複製等錯誤。
  • 鏈接。通過將所用到的結構類型、過程簽名與其聲明模塊關聯,從而發現非法調用內部過程、過程與定義不匹配等錯誤。在此過程中,會訪問全局賬本狀態。

總結

上文對 Move 語言做了簡要的解讀,希望能使讀者對 Move 語言的主要特性、設計等有基本的瞭解。本文如有錯誤,請讀者不吝指正。有關於 Move 語言的更多細節,請閱讀 Move 語言的官方技術文檔