從比特幣、以太坊、libra的不同特點認識move語言

關於比特幣、以太坊、libra,我們知道他們是不同的區塊鏈應用,那麼他們的根本差別在哪裏呢。
其實,單從白皮書的標題,就可以大概看出三個項目在設計目標上的差異。

  • 比特幣的目標是 —— 可編程貨幣(Programmable Money),所以白皮書標題是 “Bitcoin: A peer-to-peer electronic cash system”。
  • 以太坊的目標是 —— 可編程的去中心化應用(Programmable dApps),在貨幣的基礎上,擴展到更通用的領域。所以白皮書標題是:“Ethereum: a next generation smart contract and decentralized application platform”,黃皮書標題是:“Ethereum: A secure decentralized generalized transaction ledger”。
  • Libra 的設計目標恰好介於二者中間 —— 可編程資源(Programmable Resources),或者叫可編程資產。

那麼,“可編程貨幣”、“可編程應用”、“可編程資源”,這三者到底有什麼不同呢?
既然都是 “可編程 XX” 句式,他們的主要區別就在於兩點:
對什麼編程;
如何編程。
對什麼編程?

對什麼編程,是指系統所描述或者抽象的,到底是現實世界中的什麼東西。

比特幣對 “貨幣” 編程

比特幣系統抽象的是 “貨幣”,或者說是 “賬本” 的概念。貨幣可以用一個數字來描述,也就是某一個賬戶的 “餘額”。用戶可以通過 “交易”,把一部分錢轉給別人。當比特幣網絡接收到一筆交易的時候,每個節點都會檢查交易是否合法,比如你花的是不是自己的錢,有沒有足夠的餘額(比特幣不允許透支)。當這些檢查都成功後,節點會做一個簡單的加減計算:在你的賬戶中扣減轉賬的數額,並在對方賬戶中加上同樣的數量。因此,比特幣唯一的功能就是記賬,保證在賬戶彼此轉賬的過程中,貨幣的總量不會莫名其妙的增加或減少(不考慮挖礦獎勵和黑洞地址等特例)。

以太坊對 “應用” 編程

以太坊系統抽象的是 “應用”,應用的種類包羅萬象,比如遊戲、借貸系統、電商系統、交易所等,這些都是應用。理論上講,任何傳統的計算機程序都可以移植到以太坊上。因此,以太坊中記錄的是各種應用的內部數據(即 “合約狀態”),比如一個電商系統的庫存、訂單、結算信息等。這些信息無法用一個簡單的數字來描述,必須允許用戶定義非常複雜的數據結構,並且允許用戶通過代碼(智能合約),來對這些數據進行任意所需的操作。當然,這些應用也包含了 “貨幣賬本”。事實上,目前在以太坊上應用最廣泛的正是此類應用(稱爲 “ERC20 智能合約”)。由於以太坊把這類應用看作是平臺所能支持的多種應用中的一種,與其他類型的應用相比,並沒有什麼特別之處,所以也就沒有針對此類應用提供更多的安全保護,只提供了類似 ERC20 這樣的接口規範。一個在以太坊上新發行的 “貨幣”,其轉賬邏輯的正確性完全由開發者負責。
在這裏插入圖片描述
在以太坊的存儲結構中,ERC20代幣 的賬本是 “二級對象”,和 ETH 原生代幣餘額存儲在不同的地方。例如上圖所示,0x0,0x1 和 0x2 是三個以太坊地址,其中,0x0 和 0x2 是普通賬戶地址(External accounts),而 0x1 是一個合約地址(Contract accounts)。我們可以看到,每個賬戶都存儲了一個 ETH 的餘額,這個數據是頂級對象 (First-Class Object)。在合約地址 0x1 中,還存儲了一個智能合約代碼 MyCoin,它是一個 ERC20 代幣應用。而 MyCoin 這個代幣的整個賬本,都存儲在 0x1 的空間中,怎麼修改都由 0x1 中的合約代碼說了算。

無論是有意還是無意,ERC20代幣 非常容易出現安全漏洞。也就是說,在以太坊系統中,原生代幣 ETH 和用戶發行的代幣並不享有同樣的安全級別。

Libra 對 “資產” 編程

那麼,能否不那麼走極端,試圖去抽象一些比簡單數字更復雜的資產類型,而又不追求包羅萬象的 “通用性” 呢?這正是 Libra 的出發點。Libra 可以定義類似一籃子貨幣、金融衍生品等比貨幣更復雜的資產類型,以及如何對他們進行操作,這種資產被稱爲 “資源”。Move 通過限制對資源的操作來防止不恰當的修改,從而提高資產的安全性。無論資源的操作邏輯如何,都必須滿足兩個約束條件:

  • 稀缺性。即資產總量必須受控,不允許用戶隨意複製資源。通俗的說,就是允許銀行印鈔,但不允許用戶用複印機來 “製造” 新錢;
  • 權限控制。簡單的說就是資源的操作必須滿足某種預先定義的規則。例如,張三隻能花自己的錢,而不允許花李四的錢。
    在這裏插入圖片描述
    上圖是 Move 的世界狀態,與以太坊不同,它把所有資產都當作是 “一等公民”(First-Class Resources),無論是 Libra 的原生代幣,還是用戶自己發行的資產。**任何一個 “幣種” 的餘額,都存儲在用戶地址對應的空間中,對其進行操作受到嚴格的限制。**這種被稱爲資源(resource)的對象,在交易中只能被移動,而且只能移動一次,既不能被複制,也不能被消毀。甚至嚴格到在代碼中賦值給一個局部變量,而後面沒有使用它也不允許。

這種資產的存儲方式並非 Libra 獨創,在此前的一些公鏈中已有應用,例如在 Vite 公鏈中,用戶發行的幣種餘額也是頂級對象。不過 Move 可以支持更爲複雜的資產類型,並對其提供額外的保護,這是 Libra 的主要貢獻。

編者組: Vite 是本文作者創建的項目。

如何編程?

我們再來看看三個項目如何通過編程來實現豐富的擴展性。

比特幣腳本

在比特幣中,定義了一種 “比特幣腳本”,用來描述花一筆錢的規則。比特幣是基於 UTXO 模型的,只有滿足了預先定義的腳本規則,才能花費一筆 UTXO。通過比特幣腳本,可以實現 “多重簽名” 之類的複雜邏輯。比特幣腳本是一種非常簡單的基於棧的字節碼,不支持循環之類的複雜結構,也不是圖靈完備的。雖然利用它可以在比特幣網絡上發行新的貨幣(Colored Coins),但它的描述能力非常有限,對開發者也不友好,無法應用到更復雜的場景中。

以太坊的 Solidity 語言

在以太坊中,定義了一種 Solidity 的編程語言,可以用來開發 “智能合約”。智能合約代碼可以編譯成一種基於棧的字節碼 ——EVM Code,在以太坊虛擬機 EVM 中執行。Solidity 是一種高級語言,參考了 C++、Python 和 Javascript 的語法,是一種靜態類型、圖靈完備的語言,支持繼承,允許用戶自定義複雜的類型。Solidity 更像是一種通用的編程語言,理論上可以用來開發任何類型的程序,它沒有針對貨幣或者資產類型的數據,在語法和語義上做任何限制和保護。比如用它來開發一個新的代幣合約,代幣的餘額通常聲明爲 uint 類型,如果編碼時對餘額增減邏輯的處理不夠小心,就會使餘額變量發生溢出,造成超額鑄幣、隨意增發、下溢增持等嚴重錯誤, 如: BEC 智能合約的漏洞。

Libra 的 Move 語言

再來看 Libra,它定義了一種新的編程語言 Move,這種語言主要面向資產類數據,基於 Libra 所設定的 “頂級資源” 結構,主要設計目標是靈活性、安全性和可驗證性。目前,Move 高級語言的語法設計還沒有完成,白皮書只給出了 Move 的中間語言(Move IR)和 Move 字節碼定義。因此我們無法評估最終 Move 語言對開發者是否友好,但從 Move IR 的設計中,可以感受到它在安全性和可驗證性方面的特點。

Move 語言的設計

下面我們來簡單介紹一下 Move 的語法。Move 的基本封裝單元是 “模塊”(Module),模塊有點類似於以太坊中的 “智能合約”,或者面嚮對象語言中的 “類”。模塊中可以定義 “資源”(Resource)和 “過程”(Procedure),類似於類中的 “成員”(Member) 和 “方法”(Method)。

所有部署在 Libra 上的模塊都是全局的,通過類似於 Java 中的包名 + 類名的方式來引用,例如 0x001.MyModule,0x001 是一個 Libra 地址,MyModule 是一個模塊名。模塊中的過程有 public 和 private 兩種可見性,公有過程可以被其他模塊調用,私有過程只能被同模塊的過程調用。而模塊中的資源都是私有的,只有通過公有過程才能被其他模塊訪問。而且,外部模塊或者過程對本模塊資源的修改受到嚴格的限制,唯一允許的操作就是 “移動”(Move),不能隨意對資源賦值。例如,Move 中是不允許出現一個類似於 MyCoin.setBalance() 這樣的接口,讓其他用戶有機會隨意修改某個幣種餘額的。

除了受限的資源類型,Move 模塊中也允許定義非受限的成員,被稱爲非受限類型(Unrestricted Type),包括原生類型(boolean、uint64、address、bytes)和非資源類的結構體(struct)。這些非受限類型就沒有那麼嚴格的訪問限制,可以用來描述與資產無關的其他應用類數據。從這個角度來說,Move 語言理論上應該具有和 Solidity 同樣的描述能力,但由於實際的去中心化應用中,總會涉及到資產類的數據,而任何引用了資源類型的結構體也都是受限的,能夠真正脫離 Move 語言嚴格限制的機會並不多。所以在實際使用 Move 語言開發的時候,程序員一定會有一種戴着鐐銬跳舞的感覺,代碼出現編譯時和運行時失敗的可能也更大。

通俗的說,用 Move 寫代碼不會讓你感覺 “很爽”,這就是安全性和可驗證性的代價。想想你用 C 語言自己控制內存的分配和釋放時,雖然有一種 “我是上帝” 的感覺,但也會時刻憂慮緩衝區溢出、內存泄露等潛在風險;而用 Java 語言開發,雖然你不再能夠爲所欲爲的控制內存,但也不用擔心這些內存安全性問題了。自由還是安全,往往是不兼得的。

在一個 Libra 的交易(Transaction)中,也可以嵌入一段 Move 代碼,被稱爲交易腳本(Transaction Script)。這段代碼不屬於任何模塊,是一次性執行的,不能再被其他代碼調用。腳本中可以包含多個過程,通過 main 過程作爲入口來執行,在其中也可以調用其他模塊中的過程。這個設計有點類似比特幣,而和以太坊完全不同。在以太坊中,一個交易本身是不能包含一段可執行代碼的,只能部署新合約或者調用一個已部署的合約。我不太喜歡 Libra 的這個設計,由於任何 Move 代碼都必須經過字節碼驗證器(Bytecode Verifier)的嚴格檢查才能發佈到鏈上,這種一次性代碼的邊際成本遠遠高於可複用的模塊,會拖慢交易被確認的速度,降低系統的吞吐量。交易腳本並不是必須的,大部分現實場景都可以通過模塊來覆蓋,而且,它的存在還增加了 Libra 錢包的開發和使用難度,有機會的話我會向 Libra 的開發團隊提議取消這一設計。

本文內容首發於深入淺出區塊鏈技術博客,點擊解讀 Libra Move:一種可編程資源語言看一下白皮書中的示例代碼片段,直觀感受Move 語言,進一步瞭解move虛擬機等。

深入淺出區塊鏈技術博客,帶你瞭解move語言,走進libra的世界。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章