Blockore-李婷婷|作者介紹
Blockore
Blockore是以區塊鏈為核心的知識聚集教育平台,整合目前繁雜零碎的區塊鏈知識與資訊,讓認識加密貨幣與區塊鏈應用不再是難以跨出的一步,為學習者、開發者、關心區塊鏈的民眾及企業提供一優異資訊平台,將朝向「實現區塊鏈生活場域」為目標前進。
李婷婷
李婷婷現任圖靈鏈公司技術長與共同創辦人,和加州柏克萊大學區塊鏈研究院受邀學者。曾於微軟亞太區人工智能部門擔任顧問,同時亦於權威論壇 ACM、IEEE 及國際期刊 CPE 發表過三篇區塊鏈論文,並於德國慕尼黑 CryBlock 獲頒 2018 最佳論文。此外,也是知名 Medium 專欄作者筆括加密貨幣與區塊鏈,吸引全球逾 3,000 位訂閱者,並於近期開設中文區塊鏈線上課程。興趣是擔任少女寫真外拍模特 🙂
MOVE 語言的特點以及從開發人員的角度來看與以太坊的差異。
附註:部份英文專有名詞無對應中文翻譯,原文作者以及 Blockore 團隊為其保留翻譯之準確空間。
概述和動機
這篇文章帶領大家初步了解 Facebook Libra 新編程語言 Move 的 26 頁技術白皮書。 作為以太坊開發人員和區塊鏈社區愛好者,我希望為每個對這種新語言感到好奇的人,快速的介紹這篇文章,讓大家了解他的亮點 🙂
希望大家會喜歡!
1. 抽象
Move 是一種可執行的位元組碼的語言,用於執行自定義事項和智能合約。
相較於 Solidity,有兩件事需要注意:
- Move 是一種位元組碼語言,可以在 Move 的 VM 中直接執行,然而 Solidity(以太坊的智能合約語言)是一種更高級別的語言,需要在 EVM(以太坊的虛擬機)執行之前編譯成位元組碼。
- Move 不僅可用於執行智能合約,還可用於自定義事項(本文稍後將對此進行說明),而 Solidity 僅適用於以太坊上的智能合約。
The key feature of Move is the ability to define custom resource types with semantics inspired by linear logic: a resource can never be copied or implicitly discarded, only moved between program storage locations.
這是一個類似於 Rust 的功能。 Rust 中的值一次只能分配給一個名稱。如果將值分配給一個不同的名稱,那將會導致無法再使用以前的名稱訪問該值。
例如,以下代碼段將輸出錯誤: Use of moved value ‘x’ 。 這是因為 Rust 沒有垃圾回收。當變量超出範圍時,它們引用的內存也會被釋放。 簡單來說,我們可以理解為 數據 一次只能有一個「所有者」。在以下的示例中,x 是原始所有者,然後 y 成為所有者。
參考: http://squidarth.com/rc/rust/2018/05/31/rust-borrowing-and-ownership.html
2. 在開放系統中編碼數字資產
物理資產有兩個屬性難以在數字資產中編碼:
- 稀缺性:應控制系統中的資產供應。 複制現有資產應當被禁止,且創建新資產應該為一種特權。
- 資產擁有權的權限管理:系統中的參與者資產應該能受到資產控管條例的保護。
它指出了數字資產需要實現的兩個對物理資產理所當然的主要特徵。 例如,稀有金屬自然具有稀缺性 ,而傳統物理資產不需做資產擁有權的權限管理,因為花出去的錢會實體交給商家,不易有電子現金的雙重支付問題(同一張鈔票能支付多次)。
為了說明我們如何提出這兩個屬性,讓我們從以下提案開始說明:
提案#1:最簡單的規則,沒有稀缺性和權限管理
最簡單的狀態評估規則,沒有稀缺性和權限管理。
- G[K]:=n 表示將儲存在全區域區塊鏈,密鑰 K 中的值更新為 K
- transaction ⟨Alice, 100⟩表示將 Alice 的賬戶餘額設置為 100。
上述表示存在嚴重問題:
- Alice 可以通過發送 transaction ⟨Alice, 100⟩,讓自己擁有無限的硬幣。
- Alice 發送給 Bob 的硬幣毫無價值,因為 Bob 可以使用相同的技術向自己發送無限量的代幣。
提案#2:考慮到稀缺性
第二個提案只考慮稀缺性
現在我們強制將在轉移之前存儲在 ?? 下的硬幣數量設定至少為 ?。
然而,雖然這解決了稀缺問題,但是對於可以發送給 Alice 的貨幣的人沒有做所有權的檢查。 (任何人都可以根據此評估規則這樣做)
提案#3:同時考慮稀缺性和權限管理
第三個提案同時考慮稀缺性和權限管理
我們透過在檢查稀缺性之前使用數字簽章機制 verify_sig 來處理這個問題。這意味著 Alice 使用她的私鑰來簽署交易,並證明她是代幣的所有者。
2.1 現有的區塊鏈語言
現有的區塊鏈語言面臨以下問題(所有這些問題都已在 Move 中解決):
- 間接代表資產:資產使用整數進行編碼,但整數值與資產本質上並不相同。 事實上,目前沒有任何類型或數值代表 Bitcoin/Ether/StrawCoin! 這使得編寫使用資產的程序變得不便且容易出錯。 諸如將資產傳入或傳出的過程,或是將資產存儲在數據結構中等的模式會需要編程語言的一些特殊支持。
- 稀缺性是不可擴展的 :這種語言只代表一種稀缺資產。 此外,稀缺性的保護是直接在編程語言的語義中進行硬編碼而成的。 程序員在希望創建自定義資產時,必須在沒有語言支持的情況下,謹慎地實現稀缺性這一特性。
這些正是以太坊智能合約中的問題。ERC-20 代幣等自定義資產使用整數來表示其值和其總供應量。每當新的代幣被生成時,智能合約代碼必須手動檢查是否符合稀缺性(在這種情況下為總供應量)。
此外,由於資產問題的間接表示,更有可能引入重複、重用或資產損失等嚴重錯誤。
- 權限管理不靈活 :現有的模型架構,強制執行權限管理的唯一策略,是基於公鑰的簽章的模式。 與稀缺性保護一樣,權限管理的策略也深深嵌入編程語言的語義中。關於如何能擴展語言,並允許程序員制定自定義的權限管理策略,這個答案並不明顯。
在以太坊中也是如此,智能合約在使用公鑰-私鑰密碼學來進行權限管理的過程中,並沒有任何原生語言的支持。開發人員必須手動編寫權限管理,例如使用 OnlyOwner 。
儘管我是以太坊的忠實粉絲,但我同意這些資產屬性應該由語言本身支持實現,以達到安全目的。
特別是,將以太轉移到智能合約涉及動態調度,這導致了一類新的錯誤,稱為重新入侵漏洞。
動態調度在這裡指代碼執行邏輯將在運行時(動態)而不是編譯時(靜態)確定。 因此,在 Solidity 中,當合約 A 調用合約 B 的功能時,合約 B 可以運行合約 A 的設計者未預料到的代碼,這可能導致重新入侵的漏洞 (合約 A 意外執行合約 B 的功能,以便在實際扣除帳戶餘額之前提取資金)。
3. Move 的設計理念
3.1 一流的資源
在較高的層次上,Move 中 modules(模組)/resources(資源)/procedure(程序)之間的關係類似於物件導向程式中 classes/objects/methods 之間的關係。 Move 的模組類似於其他區塊鏈語言中的智能合約。 module 聲明 resource 類型和 procedure。這些資源類型和程序用於編碼創建,銷毀和更新其聲明的資源時的規則。
modules/resources/procedure 只是 Move 中的一些術語。 我們將在本文後面用一個例子來說明這些;)
3.2 靈活性
Move 通過(交易腳本)為 Libra 增加了靈活性。 每個 Libra 交易都包含一個 transaction script,它實際上是事務的主要過程。
scripts 可以執行 expressive one-off behaviors(例如支付特定的一組收件人)或 可重用行為(通過調用單個程序,此程序封裝了可重用邏輯)
從上面我們可以看出,Move 的 transcation script 引入了更多的靈活性,因為它能夠實現一次性行為以及可重用行為,而以太坊只能執行可重用行為 (這是一種只調用單一智能合約的方法)。 它被命名為「可重用 」的原因是因為智能合約功能可以多次執行。
3.3 安全
Move 的執行格式是一種類型化的(typed)位元組碼,它比彙編語言(assembly language)更高級但比源語言(source language)更低級。 位元組碼通過位元碼驗證器在鏈上檢查資源,類型和記憶體安全性,然後由位元組碼解釋器直接執行。 此選項允許 Move 提供通常與源語言相關的安全保證,但不將源編譯器添加到可信任的計算庫或編譯的成本到執行交易的關鍵路徑 。
對於 Move 來說 ,成為位元組碼語言是一種非常簡潔的設計。 由於它不需要像 Solidity 一樣,從源代碼編譯為位元組碼,因此不必擔心在編譯器中可能出現的故障或攻擊。
3.4 可驗證性
我們的方法是盡可能多地執行關鍵安全屬性的輕量級的鏈上驗證,但同時設計 Move 可支持進階鏈下的靜態驗證工具。
從這裡我們可以看到 Move 更喜歡執行靜態驗證而不是進行鏈上驗證。儘管如此,正如他們在論文末尾所述,驗證工具留在未來進行開發。
3.5 模組化
Move 模組強制執行 data abstraction(數據抽象)並將在資源上的關鍵操作本地化。 模組利用的封裝,結合 Move 系統強制執行的保護,可確保模組類型的屬性不會受到模組外部的代碼的影響。
這也是一個非常好的數據抽象設計! 這意味著智能合約中的數據只能在合約範圍內修改,而不能從外部修改。
來自: https://libra.org/en-US/open-source-developers/#move_carousel
4. Move 概述
此示例的交易腳本演示了一個惡意或粗心程序員,在模組外部不能違反模組資源的關鍵安全不變量。
本節將透過一個示例,向您介紹在編寫編程語言時的 modules(模組),resources(資源)和procedure(程序)到底是什麼。
4.1 點對點付款交易腳本
貨幣金額將從交易發送方轉移到收款方
這裡有幾個新符號(紅色小文字是我自己的筆記 XD):
- 0x0 :存儲 module 的帳戶地址
- Currency :module 的名稱
- Coin :resource 類型
- coin 是一個被 procedure 返回的值。它是一個類型為 0x0.Currency.Coin 的resource 值
- move() :該值不能再次使用
- copy() :該值可以在以後使用
代碼細部 :
在第一步中,發送方從存儲在 0x0.Currency 的 module 中,調用名為 withdraw_from_sender 的 procedure。
在第二步中,發送者通過將貨幣的資源值移動到 0x0.Currency module 的存款這動作,將資金轉移到收款人。
以下是 3 種將被拒絕的代碼示例:
1. 將 move(coin)改為 copy(coin)來複製貨幣金額
資源值只能被移動。 嘗試複製資源值(例如,在上面的示例中使用 copy(coin) )將導致在位元組碼驗證時出錯。
因為 coin 是資源值,所以它只能被移動。
2. 透過兩次 move(coin) 來重新使用貨幣
將 0x0.Currency.deposit(copy(some_other_payee),move(coin))添加到上面的示例中會讓發件人“花”兩次貨幣 – 第一次與收款人,第二次與 some_other_payee。 這種不良行為在物理資產的情況下無法實現。 幸運的是,Move 不會允許此類計劃的實施。
3. 透過刪除 move(coin) 丟失貨幣
移動資源失敗時(例如,通過將上面示例中包含 move(coin) 的那行代碼刪除)將觸發位元組碼驗證錯誤。 這可以防止使用 Move 的程序員不會意外地或有意地丟失對資源的跟踪。
4.2 Currency(貨幣)模組
4.2.1 入門開始:Move execution model(執行模型)
每個帳戶可以包含零個或多個模組(為上圖矩形)和一個或多個資源值(為上圖圓柱體)。 例如,在地址 0x0 的帳戶有模組 0x0.Currency 和類型為 0x0.Currency.Coin 的資源值。 在地址 0x1 的帳戶有兩個資源值和一個模組; 在地址 0x2 的帳戶有兩個模組和一個資源值。
一些值得注意的點:
- 執行交易腳本是全有或全無的
- 模組是在全域狀態下發布的長期代碼
- 全域狀態的結構為從帳戶地址到帳戶的 map
- 帳戶最多只能包含一個給定類型的資源值,和一個具有給定名稱的模組(在地址 0x0 的帳戶不允許包含其他的類型為 0x0.Currency.Coin 的資源或另一個名為 Currency 的模組)
- declaring module(聲明模組)的地址算是類型的一部分( 0x0.Currency.Coin 和 0x1.Currency.Coin 是不能互換使用的不同類型)
- 程序員仍然可以通過定義自定義一個包裝好的資源,來保有帳戶中給定資源類型的多個實例( resource TwoCoins { c1: 0x0.Currency.Coin, c2: 0x0.Currency.Coin } )
- 規則是可以的,只要您仍然可以通過其名稱引用資源而不會發生衝突,例如您可以使用 TwoCoins.c1 和 TwoCoins.c2 引用這兩個資源。
4.2.2 聲明貨幣的資源
一個名為 Currency 的模塊組和一個由模組管理的名為 Coin 的資源類型
一些值得注意的點:
- Coin 是一種結構類型,其單個字段(field)值類型為 u64(64 位無符號整數)
- 只有 Currency module(模組)的procedure(程序)才能創建或銷毀 Coin 類型的值
- 其他 module(模組)和 transaction script(交易腳本)只能透過模組公開的 procedure(程序)寫入或引用值的字段
4.2.3 實施存款
此過程將 Coin(貨幣)資源作為 input(輸入),並透過以下步驟將其與存儲在收款人帳戶中的 Coin 資源組合:
- 銷毀輸入 Coin 並記錄其值。
- 獲取對存儲在收款人帳戶下的獨一的 Coin 資源的引用。
- 通過將 Coin 的值傳遞給 procedure(程序),來增加收款人 Coin 的值。
一些值得注意的點:
- Unpack, BorrowGlobal 是內置 procedure(程序)
- Unpack<T>是刪除類型為 T 的資源的唯一方法。它將類型為 T 的資源作為輸入,銷毀它,並返回綁定到資源的字段的值
- BorrowGlobal<T>將地址作為輸入,並返回對在此地址下發布的唯一類型 T 實例的引用 &mut Coin 是對 Coin 資源的可變引用,而不是對 Coin
4.2.4 實現 withdraw_from_sender
這個程序(procedure):
- 獲取對在發件人帳戶下發布的 Coin 類型的唯一資源的引用。
- 透過輸入量來減少引用的 Coin 的值。
- 創建並返回值為金額的新 Coin。
一些值得注意的點:
- 任何人都可以調用 Deposit ,但 withdraw_from_sender 有限制,它只能被硬幣所有者調用的權限管理
- GetTxnSenderAddress 類似於 Solidity 裡的 msg.sender
- RejectUnless 類似於 Solidity 裡的 require 。 如果此檢查失敗,則當前交易腳本的執行將停止,並且它執行的任何操作都不會應用於全域狀態
- Pack<T>,也是一個內置 procedure(過程) ,它用於創建一個 T 類型的新資源與 Unpack<T> 相似 , Pack<T> 只能在資源 T 的聲明模組內調用
總結
現在您已經初步了解了 Move 的主要特徵,它與以太坊的相較的區別,以及其基本語法。
最後,我強烈建議您閱讀白皮書的原文 。 它包含了許多關於編程語言設計原則的細節以及許多很好的參考資料。
非常感謝您的閱讀時間。 歡迎任何建議!
《Ting Ting Lee 李婷婷》授權 Blockore 轉譯
(本文經作者同意轉載,文內觀點皆代表原作者,不代表桑幣筆記 Zombit 立場)