Libra的Move程式語言到底是個啥? 美女程式設計師通讀26頁的白皮書後, 找出了這些精華… | 技術頭條

買賣虛擬貨幣

作者 | Lee Ting Ting

編譯 | Guoxi

責編 | Aholiab

出品 | 區塊鏈大本營(blockchain_camp)

自去年礦難以來,業界充滿了對區塊鏈唱衰的聲音,鏈圈有很多人都開始對區塊鏈的價值產生懷疑。而 Facebook 不斷爆出的區塊鏈專案的訊息可謂是給鏈圈打了一劑強心針,盼望著,盼望著,今年 6 月,Facebook 釋出了加密貨幣 Libra 的白皮書並上線了官網。可以預見, Libra 離落地不遠了。

Libra 以非營利組織的形式管理,其創始成員包括大名鼎鼎的銀行卡巨頭 Visa ,萬事達,以及傳統線上支付巨頭 PayPal ,頗有挑戰當下全球金融貨幣體系的趨勢。

Libra最近越來越火。不少開發人員已經開始跟進 Libra 專案,希望在這個全球性的區塊鏈專案中搶佔先機。而 Libra 的程式語言 Move 則是開發人員眼中的重中之重

為了幫助開發人員做好入門工作,加州大學伯克利分校區塊鏈實驗室研究學者,Turing Chain 聯合創始人兼技術長 Lee Ting Ting 從開發人員的角度分析了 Move 語言的典型特徵以及它與以太坊 Solidity 的異同,為我們帶來了這篇 Move 語言的入門指南。

Move語言的白皮書長達26頁,本篇文章是對該白皮書的精華介紹,文中也會直接放出一些白皮書上的原文。Move 是 Facebook 公司為其加密貨幣產品 Libra 開發的全新程式語言。

作為開發人員和區塊鏈社羣愛好者,希望透過這篇文章幫助你快速入門 Move 語言。

關於Move

Move 是一種用於實現 Libra 自定義交易和智慧合約的可執行的位元組碼語言。

它與以太坊 Solidity 語言有以下兩個區別

  1. Move 是一種位元組碼語言,它可以直接在 Move 虛擬機器中執行,而以太坊的 Solidity 語言是一種更高階別的語言,它需要先編譯成位元組碼再加入到以太坊虛擬機器中執行。

  2. Move 語言不僅可以用來實現智慧合約,還可以用來實現自定義交易(彆著急,接下來我們會詳細介紹自定義交易),而 Solidity 語言只能用來實現以太坊中的智慧合約。

Move 語言的一個關鍵特性是它能夠定義受線性邏輯啟發的帶語義的自定義資源型別。在這種情況下資源永遠不會被複制或被隱式丟棄,它只能在程式的儲存位置之間轉移。

這是一個與 Rust 語言類似的特性。Rust 語言中的數值一次只能分配給一個命名。如果你將某個數值分配給其他命名則將無法再使用之前的命名訪問到該數值。

就比如說,下面這段程式碼將會輸出一個錯誤:使用轉移了的值“ x ”(Use of moved value ‘x’)。

這是因為 Rust 語言沒有垃圾回收機制。當變數超出範圍時,變數引用的記憶體也會被釋放。

這樣解釋有點麻煩,為了簡單起見,我們可以這樣來理解,每個資料在同一時間內只能有一個“所有者”。在這個例子中,x 是資料初始的所有者,後來 y 也成了資料的所有者,所以程式會報錯,如下面程式碼所示。

在開放系統中編碼數字資產

將現實世界中的物理資產編碼成區塊鏈上的數字資產,主要存在兩大難題:

  • 稀缺性:區塊鏈系統中資產的供應應該受到嚴格管控。應該禁止複製現有的資產,同時也應該禁止普通使用者隨意建立新資產。

  • 訪問控制:區塊鏈系統中的參與者應該能夠使用訪問控制策略保護自己的資產。

這也是所有數字資產都需要實現的兩個關鍵特性,這些特性被認為是現實世界中物理資產的自然表徵。就比如說,現實世界中稀有金屬就是很稀缺的,在現實世界中你只能花屬於自己的錢,換句話說就是自己有訪問許可權的錢。

為了更好地闡述這兩個關鍵特性是如何實現的,讓我們先從以下三個示例說起:

示例1:不考慮稀缺性和訪問控制的最簡單的規則

左邊為交易指令碼的格式,右邊為區塊鏈狀態的評估規則

  • G [K]:= n 表示使用加密貨幣數額 n 來更新賬戶 K 在區塊鏈全域性狀態中儲存的加密貨幣餘額。

  • transaction⟨Alice,100⟩ 表示將 Alice 的賬戶餘額設定為 100 。

上述的實現方式存在兩個很嚴重的問題:

  • Alice 可以透過不斷髮起交易 transaction⟨Alice,100⟩ 讓自己擁有無限多的加密貨幣。

  • Alice 與 Bob 之間的加密貨幣轉賬也變得毫無意義,因為 Bob 也可以使用相同的手段向自己傳送無限多的加密貨幣。

示例2:在數字資產中加入稀缺性

左邊為交易指令碼的格式,右邊為區塊鏈狀態的評估規則

現在我們強制要求在交易發起時發起方 Ka 的賬戶餘額至少為交易的金額 n 。

雖然這種實現方式可以解決稀缺性的問題,但是現在還存在一個問題,就是你可以將任何人的加密貨幣餘額轉給自己,這是因為我們還沒有加入對誰能發起交易的檢查,也就是對加密貨幣所有權的檢查。

示例3:在數字資產中同時加入稀缺性和訪問控制

左邊為交易指令碼的格式,右邊為區塊鏈狀態的評估規則

為了實現加密貨幣的訪問控制,我們可以在稀缺性檢查之前使用數字簽名機制 verify_sig 來檢查所交易加密貨幣的所有者,這意味著 Alice 可以使用她的私鑰來簽署交易並證明她是所交易加密貨幣的所有者。

現有的區塊鏈程式語言

現有的區塊鏈程式語言往往都會被以下問題所困擾,令人欣慰的是,Move 語言完美地解決了所有這些問題。主要體現在以下兩方面。

  1. 間接地表示資產有些區塊鏈程式語言使用整數對數字資產進行編碼。這種編碼方式十分牽強,因為這些整數值與數字資產根本就不是一回事。事實上,這些區塊鏈中並沒有任何型別或數值來表示比特幣/以太幣/山寨幣!這使得編寫與數字資產互動的智慧合約變得十分笨拙且容易出錯。在這些區塊鏈中實現諸如資產轉入/轉出以及將資產儲存在資料結構中這樣的操作都需要特殊的語言支援。

  2. 稀缺性是不可擴充套件的這些語言往往只能表示一種稀缺資產。除此之外,稀缺性保護直接在語言語義中進行硬編碼。開發人員如果想要建立一個自定義的資產,遺憾的是他得不到該語言的一絲幫助,他將不得不重複造輪子,重新實現資產的稀缺性。

相信你可能已經看出來了,這些正是以太坊智慧合約中存在的問題。ERC-20 通證等自定義資產使用整數來表示資產和總供應量。每當生成新的通證時,智慧合約程式碼必須手動檢查交易是否滿足稀缺性(在這種情況下為是否超過總供應量)。

此外,資產的間接表示會給區塊鏈帶來很多的問題,就比如說資產複製、資產重複使用、資產意外丟失等漏洞。

當然,除了上面兩點以外,還包括:訪問控制不夠靈活

這些區塊鏈強制執行的訪問控制策略只有基於公鑰的數字簽名方案。與稀缺性保護一樣,訪問控制策略也被深深嵌入到語言語義中。

如果開發人員想要設定自定義的訪問控制策略,那麼他還是會陷入重複造輪子的困境。

以太坊也存在這樣的問題。以太坊智慧合約缺乏對使用公鑰私鑰密碼學實現訪問控制的本地語言支援。面對這種需求。開發人員不得不手動編寫訪問控制,就比如說使用 OnlyOwner函式。

儘管我是以太坊的忠實粉絲,但我堅持認為以太坊在這些資產屬性方面存在欠缺。從安全方面考慮,這些資產屬性本應得到原生的語言支援。

特別是,將以太坊轉移到智慧合約中需要用到動態分派( dynamic dispatch ,處理程式語言的語言方法呼叫的一種計算機制),這又會帶來一類新的漏洞:可重入性漏洞( re-entrancy vulnerabilities )。

這裡的動態分派意味著程式碼的執行邏輯將在程式碼執行時(動態)確定,而不是在程式碼編譯時(靜態)確定。

因此,在 Solidity 語言中,當智慧合約 A 呼叫智慧合約 B 的函式時,智慧合約 B 可能會執行智慧合約 A 的設計者從未預料到的程式碼,這可能會導致可重入性的漏洞(智慧合約 A 意外執行智慧合約 B 的函式,從而在實際更新賬戶餘額之前提取到資金)。

Move 語言的設計目標

一流的資源

從較高的層次上來說,Move 語言中模組/資源/程式之間的關係類似於面嚮物件語言中的類/物件/方法之間的關係。

Move 語言中的模組類似於其他區塊鏈語言中的智慧合約。模組宣告資源型別和程式,而這些資源型別和程式編碼用於建立,銷燬和更新所宣告資源的規則。

模組/資源/程式只是 Move 語言中的一些術語。下文中我們將會用一個例子來介紹它們。

靈活性

Move 透過交易指令碼為 Libra 增加了很多靈活性。每筆 Libra 交易都包含一個交易指令碼,該指令碼實際上是交易的核心。

交易指令碼可以用來執行一次性的行為(例如給一組特定的收款人付款),也可以用來執行可重用的行為(透過呼叫一個封裝了可重用邏輯的程式)。

從上面我們可以看出,Move 的交易指令碼透過同時支援一次性的行為和可重用的行為為 Libra 引入了更多的靈活性,而以太坊只能執行可重用的行為(即呼叫單個智慧合約方法)。

以太坊被稱為“可重用”的原因是智慧合約中的函式可以被多次執行。

安全性

Move 的可執行格式是一種型別化的位元組碼,它比組合語言更高階但比源語言更低階。在區塊鏈上位元組碼驗證器會檢查位元組碼的資源,型別以及記憶體安全性,然後位元組碼直譯器會直接執行位元組碼。這種設定使得 Move 在提供與源語言相關聯的安全保證的同時,省去了將源編譯器新增到可信計算基礎( Trusted Computing Base,TCB )以及編譯到交易執行的關鍵路徑的成本。

將 Move 構建成一種位元組碼語言確實是一種非常簡潔的設計。由於它不需要像 Solidity 一樣從原始碼編譯成位元組碼,因此不必擔心編譯器中可能出現的故障或漏洞。

可驗證性

我們的方法是儘可能多地在區塊鏈上執行核心安全屬性的輕量級驗證,但同時我們也在 Move 語言中加入了對鏈下高階靜態驗證工具的支援。

從這裡我們可以看出 Move 更傾向於執行靜態驗證而不是在區塊鏈上執行驗證工作。儘管如此,正如白皮書末尾所述,Libra 團隊未來將會開發完善驗證工具。

模組化

Move 模組強制執行資料抽象並本地化執行資源的關鍵性操作。模組啟用的封裝與 Move 型別系統強制執行的保護相結合,強強聯手可以確保模組外部的程式碼不能違反模組型別規定的屬性。

這是一個非常好的資料抽象設計!這意味著智慧合約中的資料只能在智慧合約範圍內修改,而不能在外部修改。

Move語言實操

這個交易指令碼的示例說明了模組外部的惡意開發人員或粗心的開發人員不可能違反模組資源的關鍵安全不變性。

這一部分中我們將討論在進行 Move 語言開發時,實際使用到的模組、資源和程式分別是什麼東西。

點對點支付交易指令碼

Move語言的點對點支付交易指令碼,如下面程式碼所示:

amount(金額)表示所交易加密貨幣的金額,這些加密貨幣將從交易的發起方轉移給接收方 payee。

程式碼中有幾個新的符號,其中紅色的小字是我記的筆記:

  • 0x0:儲存模組的帳戶地址

  • currency:模組的名稱

  • coin:資源型別

  • 程式返回的 coin 值是一個型別為 0x0.Currency.Coin 的資源值

  • move():該值不能再次使用

  • copy():該值可以再次使用

程式碼功能解讀:

在第一步中,傳送方從儲存在 0x0.Currency 的模組中呼叫了名為 withdraw_from_sender 的程式。

在第二步中,傳送方透過將加密貨幣的資源值轉移到 0x0.Currency 模組的存款程式中從而將資金轉移給收款人。

以下是三種會報錯的程式碼示例:

1. 透過將轉移加密貨幣 move(coin) 替換為複製加密貨幣 copy(coin) 來複制加密貨幣。

資源值只能被轉移。嘗試複製資源值(就比如說示例中使用的複製加密貨幣 copy(coin) )將在位元組碼驗證時引起錯誤。

因為 coin 是一個資源值,所以它只能被轉移。

2. 透過兩次轉移加密貨幣 move(coin) 來重複使用加密貨幣(雙重支付)。

在上述的示例程式碼中加入一行:

0x0.Currency.deposit(copy(some_other_payee),move(coin))

就可以讓傳送方兩次花費同一筆加密貨幣,第一次交易的收款人是 payee ,第二次交易的收款人是 some_other_payee 。現實生活中的物理資產可以完全杜絕雙重支付,幸運的是,Move 也可以做到。

3. 忘記執行轉移加密貨幣 move(coin) 導致加密貨幣丟失。

忘記轉移資源(就比如說刪除上述程式碼示例中轉移加密貨幣 move(coin) 所在的行)將觸發位元組碼驗證錯誤。這種機制可以保護 Move 開發人員不會有意或無意地丟失資源。

貨幣 Currency 模組

模組入門:Move 語言的執行模型

三個賬戶的區塊鏈全域性狀態示例

每個帳戶可以擁有零個或多個模組(上圖中的矩形)和一個或多個資源值(上圖中的圓柱體)。就比如說,地址 0x0 處的帳戶擁有一個名為 0x0.Currency 的模組和一個 0x0.Currency.Coin 型別的資源值。地址 0x1 處的帳戶擁有兩個資源值和一個模組;地址 0x2 處的帳戶擁有兩個模組和一個資源值。

需要注意的是:

  • 交易指令碼的執行只有兩種結果:成功或是失敗,不會存在中間的狀態。

  • 模組是在區塊鏈全域性狀態中釋出的長期存在的程式碼。

  • 區塊鏈全域性狀態的結構為從帳戶地址到帳戶的對映。

  • 帳戶最多隻能包含一個給定型別的資源值,並且最多隻能包含一個具有給定名稱的模組(就比如說,上圖中地址 0x0 處的帳戶不能再擁有一個額外的 0x0.Currency.Coin 資源或另一個名為 Currency 的模組)。

  • 所宣告模組的地址是型別的一部分(就比如說,0x0.Currency.Coin 和 0x1.Currency.Coin 是不能互換使用的不同型別)。

  • 開發人員仍然可以透過自定義的包裝器( wrapper )資源來實現一個帳戶擁有多個給定資源型別的例項。(resource TwoCoins { c1: 0x0.Currency.Coin, c2: 0x0.Currency.Coin })

  • 開發人員仍然可以透過名稱引用資源而不會產生任何衝突,就比如說,你可以使用 TwoCoins.c1 和 TwoCoins.c2 這兩個名稱引用這兩個資源。

宣告加密貨幣資源:

在名為 Currency(貨幣)的模組中定義一個由模組管理的名為 Coin(加密貨幣)的資源型別。

需要注意的是:

  • Coin(加密貨幣)是一種結構型別,其欄位容許的值型別為 u64 (64位無符號整數)。

  • 只有 Currency (貨幣)模組的程式能夠建立或銷燬 coin(加密貨幣)型別的值。

  • 其他模組和交易指令碼只能透過模組提供的公共可訪問的程式來寫入或引用值欄位。

實現存款操作

這段程式將 Coin(加密貨幣)資源作為輸入,並將其與儲存在收款人 payee 帳戶中的 Coin 資源組合,具體的步驟如下:

  1. 銷燬輸入的加密貨幣並記錄其數值。

  2. 獲取對儲存在收款人帳戶下的 Coin 資源的唯一引用。

  3. 將程式傳遞過來的加密貨幣的數值加到收款人賬戶餘額中,並更新收款人賬戶餘額。

需要注意的是:

  • Unpack,BorrowGlobal 是內建程式。

  • Unpack <T> 是唯一一種刪除型別為 T 的資源的方法。它將型別為 T 的資源作為輸入,刪除它,並返回繫結到資源欄位的數值。

  • BorrowGlobal <T> 將地址作為輸入,並返回對該地址下唯一的 T 例項的引用。

  • &mut Coin 是對 Coin 資源的可變引用,而不是對 Coin 。

實現撤銷存款 withdraw_from_sender:

這個程式分為三步:

  1. 獲取對傳送方帳戶下唯一的 Coin 型別資源的引用。

  2. 用輸入的數額減少引用的 Coin 的數值。

  3. 建立並返回值為更新後金額的新加密貨幣。

需要注意的是:

  • 任何人都可以呼叫存款函式 deposit ,但撤銷存款函式 withdraw_from_sender 具有訪問控制策略,因而只能被加密貨幣的所有者呼叫。

  • 獲取交易發起人賬戶函式 GetTxnSenderAddress 類似於 Solidity 語言中的 msg.sender 。

  • 除非滿足什麼條件否則就拒絕函式 RejectUnless 類似於 Solidity 語言中的 require 。如果此項檢查失敗,則當前交易指令碼會停止執行,並且它執行的任何操作都不會更新區塊鏈全域性狀態。

  • Pack <T> 也是一個內建程式,它主要用來建立一個 T 型別的新資源。

  • 與 Unpack <T> 一樣, Pack <T> 只能在資源 T 的宣告模組中呼叫。

寫在最後

現在你已經瞭解了 Move 語言的主要特徵,基本語法以及它與以太坊的差異。

最後,如果你想從事 Move 語言開發,我強烈建議你閱讀 Move 語言原始的白皮書。白皮書中包含許多 Move 語言的設計原則以及許多很好的參考資料。

免責聲明:

  1. 本文版權歸原作者所有,僅代表作者本人觀點,不代表鏈報觀點或立場。
  2. 如發現文章、圖片等侵權行爲,侵權責任將由作者本人承擔。
  3. 鏈報僅提供相關項目信息,不構成任何投資建議

推荐阅读

;