最近,關于 Aptos 和 Sui 的討論如火如荼,兩者是新興的高性能 L1 公鏈,Move 智能合約編程語言是這些新鏈必不可缺的組成部分。一些開發人員正在積極轉向Move,宣稱它是智能合約發展的未來。其他人更加謹慎,認為 Move 與現有編程語言相比,不能提供更多太新的東西。
原文標題:《Smart Contract Development—Move vs.Rust》
作者:Kre?imir Klas,Move 語言創始人
編譯:郭倩雯,鏈捕手
最近,關于 Aptos 和 Sui 的討論如火如荼,兩者是新興的高性能 L1 公鏈,Move 智能合約編程語言是這些新鏈必不可缺的組成部分。一些開發人員正在積極轉向Move,宣稱它是智能合約發展的未來。其他人更加謹慎,認為 Move 與現有編程語言相比,不能提供更多太新的東西。
加密投資者也在好奇這些 L1 公鏈的獨特之處,如何能與 Solana 抗衡,后者是目前高性能 L1 的主要玩家,以使用 Rust 作為智能合約編程而著稱。
但目前我們看到的討論并沒有達到一定深度,能夠真正參透這些新科技對我們的影響。這在討論的兩極都適用——Move 的質疑者將 Move 貶低得一無是處,無法欣賞到它更細微(但十分重要)的一面,但 Move 的支持者,過度鼓吹 Move,也沒有能看透究竟是什么使其偉大。這就帶來巨大的中間地帶與模糊不清,致使外界看客、加密開發人員、投資人員,關注此話題,但又無法對自己的觀點確信。
在這篇文章中,我將對 Move、其新穎的編程模型、Sui 區塊鏈和它如何利用 Move 的功能,以及它與 Solana 及其編程模型的比較進行深入的技術挖掘。為了突出 Move 的特點,我將把 Solana/Rust 與 Sui/Move 進行比較。因為當你把一個東西與另一個你已熟悉的東西進行比較時,理解就會更容易。
Move 還有其他變種,如 Aptos Move,它們在某些方面略有不同。本文的重點不是討論 Move 不同變體之間的細微差別,而是展示 Move 的普遍優勢,以及它與 Solana 編程模型的比較。因此為了簡單起見,我在本文中只使用一個變體(Sui Move)。因此,我在本文中介紹的某些Move概念(即對象和相關功能)只適用于 Move 的 Sui 變體,而不適用于其他變體。雖然 Move 的其他變體不一定有這些概念,但它們使用不同的機制(例如全局存儲)能夠實現同樣功能。但即便如此,本文所討論的所有Move的主要優點都適用于所有Move集成(原生設定上支持Move字節碼 Move bytecode),包括Aptos。我選擇Sui,只是因為我對它更熟悉,且我覺得它更直觀一些,更容易以文章的形式呈現。
在Solana上,程序(智能合約)是無狀態的,它們自己不能訪問(讀或寫)任何在整個交易中持續存在的狀態。為了訪問或保持狀態,程序需要使用賬戶。每個賬戶都有一個唯一的地址(Ed25519密鑰對的公鑰),可以存儲任意的數據。
我們可以把Solana的賬戶空間看作是一個全球鍵值存儲,其中鍵是賬戶地址(pubkey),值是賬戶數據。程序通過讀取和修改其值在該鍵值存儲上進行操作。
賬戶有一個所有權的概念。每個賬戶由一個(且只有一個)程序擁有。當一個賬戶被一個程序擁有時,該程序被允許修改其數據。程序不能修改所不擁有的賬戶(但允許讀取這些賬戶)。運行期間,比較程序執行前后的賬戶狀態,就能夠進行這種動態檢查,若有非法改動,則交易失敗。
每個賬戶也有一個與之相關的私鑰(相應的公鑰是它的地址),能夠訪問這個私鑰的用戶可以用它來簽署交易。利用這種機制,我們在Solana智能合約中實現了權限和所有權的功能--例如,為了獲取某些資金,智能合約可以要求用戶提供必要的簽名。
在其他做程序調用時,客戶需要指定這個程序在調用時將訪問哪些賬戶。這樣一來,交易處理運行時間就可以安排不重迭的交易并行執行,同時保證數據一致性。這是Solana的設計特點之一,使其具有高吞吐量。
程序可以通過CPI調用來調用其他程序。這些調用的工作原理與來自客戶端的調用基本相同——調用者程序需要指定被調用者程序將訪問的賬戶,被調用者程序將進行輸入檢查,就和從客戶端調用是一樣的(因為它不信任調用者程序)。
PDA賬戶是一種特殊賬戶,使程序能在不擁有或儲存私鑰的情況下提供賬戶簽名。PDA保證只有為其生成PDA的程序可以為其創建一個簽名(而其他用戶和程序不行)。當一個程序需要通過CPI調用與另一個程序進行交互并提供授權時,這是很有用的(例如,實施一個金庫)。PDA保證除了程序之外沒有人可以直接訪問程序資源。PDA也可用于在確定地址創建賬戶。
這些是Solana上安全智能合約編程的基本構件。在某種程度上,你可以把Solana程序看作是操作系統中的程序,而賬戶則是文件,任何人都可以自由執行任何程序,甚至部署自己的程序。當程序(智能合約)運行時,它們將讀取和寫入文件(賬戶)。所有文件都可被所有程序讀取,但只有對文件有所有權權限的程序才可以對其進行改寫。程序也可以執行其他程序,但它們彼此之間沒有任何信任——無論誰執行程序,它都需要假設輸入是潛在惡意的。由于該操作系統是任何人在全球范圍內都訪問的,所以在程序中加入了原生簽名驗證支持,以便為用戶實現權限和所有權功能......這不是一個完美的比喻,但還是挺有趣的。
在Move中,智能合約是以模塊形式發布的。模塊由函數和自定義類型(結構/struct)組成。結構由字段組成,可以是原始類型(u8,u64,bool...)或其他結構。函數可以調用其他函數——可以是同一模塊,也可以是其他公開的模塊。
在Solana中,這就相當于所有智能合約都作為模塊發布在一個程序中。這意味著所有的智能合約(模塊)都包含在同一類系統中,可以直接相互調用,而不需要通過中間的API或接口。這一點非常重要,其影響將在本文中徹底討論。
3.1. 對象
要注意的是,下面的對象概念針對于Move的Sui變體。而在Move的其他集成中(例如Aptos或Diem/core Move),情況可能略有不同。不過,在其他Move變體中也有類似的解決方案,可以實現同樣的事情(狀態的持久性),這些解決方案并沒有太大區別。
這里介紹Sui變體的主要原因是,文章后面的代碼樣本都基于Move的Sui變體,同時其對象比如core Move中的全局存儲機制更直觀易懂一點。重要的是,本文所討論的Move的所有主要優點都適用于所有Move集成(原生支持Move字節碼),包括Aptos。
對象是由運行時存儲的結構實例(struct instance),并在事務中持續保持狀態。
有三種不同類型的對象(在Sui中):
- 自有對象(owned objects)
- 共享對象(shared objects)
- 不可變對象(immutable objects)
自有對象是屬于用戶的對象。只有擁有該對象的用戶才能在交易中使用它。所有權元數據是完全透明的,由運行處理。它使用公鑰加密技術實現——每個自有對象都與一個公鑰關聯(運行時存儲在對象元數據中),任何時候你想在交易中使用對象,你都需要提供相應簽名(現在支持Ed25519,即將支持ECDSA和K-of-N多簽名)。
共享對象類似于自有對象,但它們沒有一個與之相關的所有者。因此,你不需要擁有任何私鑰就可以在交易中使用它們(任何人都可以使用它們)。任何自有對象都可以被共享(由其所有者),一旦一個對象被共享,它將永遠保持共享——永遠不能被轉移或再次成為自有對象。
企業級區塊鏈平臺SettleMint推出AI助手,以輔助Web3開發人員編寫智能合約:金色財經報道,企業級區塊鏈平臺SettleMint最近在其平臺上推出AI助手,以幫助Web3開發人員編寫智能合約。SettleMint聯合創始人兼首席技術官Roderik van der Veer表示,SettleMint創造人工智能助手并不是為了取代人類,人工智能可以幫助起草智能合約,但其真正的價值在于解釋每一行代碼的作用。SettleMint主要在OpenAI的GPT-4上對人工智能助手進行培訓,并向其提供有關區塊鏈開發的最新信息。[2023/8/3 16:16:10]
不可變對象是不能被改動的對象。一旦一個對象被標記為不可變,它的字段就不能再被修改。與共享對象類似,這些對象沒有所有者,可以被任何人使用。
Move編程模型非常直觀和簡單。每個智能合約是一個模塊,由函數和結構定義組成。結構在函數中被實例化,并可以通過函數調用傳遞到其他模塊。為了使一個結構能夠在跨交易中保持持久,我們把它變成一個可以被擁有、共享或不可改變的對象(僅限于Sui,在其他Move變體中略有不同)。
我們已經看到,在Move:
- 你可以將你擁有(或共享)的任何對象傳遞給任何模塊中的任何函數
- 任何人都可以發布一個(潛在的敵對)模塊
- 不存在模塊擁有結構的概念,這將使所有者模塊擁有改變結構的唯一權力,就像Solana賬戶的情況一樣——結構可以流入其他模塊,也可以嵌入其他結構中。
問題是,這種做法為什么是安全的?是什么阻止了人們發布惡意模塊,獲取共享對象(如AMM池),并將其發送到惡意模塊中,然后繼續耗盡其資金?
在Solana中,有一個賬戶所有權的概念,也就是說只有擁有賬戶的程序才被允許對其進行改動。但是在Move中,沒有模塊擁有對象的概念,你可以將對象發送到任意的模塊中——不僅可以引用對象、整個對象,也可以引用其本身價值。而且,運行時也沒有具體檢查,以確保這個對象在通過不受信模塊時沒有被非法修改。那么,是什么在保護這個對象的安全?如何保證這個對象不被不可信的代碼濫用?
這就是Move的新穎之處......讓我們來談談資源。
4.1. 結構
定義一個結構(struct)類型和你所期望的差不多:
到目前為止還不錯——這也是你在Rust中定義一個結構的方式。但在Move中,結構有其獨特之處與傳統編程語言相比,Move模塊在如何使用類型上擁有更多空間。在上面的代碼片斷中定義的結構將受以下限制:
- 它只能在定義該結構的模塊中被實例化(“打包”)和銷毀(“解包”)——也就是說,你不能從任何其他模塊的任何函數中實例化或銷毀一個結構實例。
- 結構實例的字段只能在其模塊中被訪問(因此也可以被改動)。
- 不能在其模塊之外克隆或復制結構實例
- 不能將一個結構實例存儲在其他結構實例的字段中
這意味著,如果在其他模塊的函數中處理這個結構的實例,我們將無法改動其字段、克隆它、將其存儲在其他結構的字段中,或將其丟棄(必須通過函數調用將其傳遞到其他地方)。情況是這樣的:該結構的模塊實施了能從我們模塊中調用的函數,來完成這些事情。但除此之外,我們無法直接為外部類型做這些事情。這使模塊可以完全控制如何使用和不使用其類型。
由于這些限制,我們似乎失去很多靈活性。這也是事實——在傳統編程中,處理這樣的結構會非常麻煩,但事實上,這正是我們在智能合約中想要的。智能合約開發畢竟是關于數字資產(資源)的編程。如果你看一下上面描述的結構,這正是它的本質——它是一種資源。它不能隨意被憑空創造,不能被復制,也不能被意外地銷毀。因此,我們確實在這里失去了一些靈活性,但我們失去的靈活性正是我們所希望的,因為這使對資源的操作變得直觀而安全。
此外,Move允許我們通過向結構添加能力(capability)來放寬其中一些限制。有四種能力:鍵、存儲、復制和刪除。你可以將這些能力的任何組合添加到一個結構中。
下面是它們的作用:
- 鍵 - 允許一個結構成為一個對象(專屬Sui,core Move情況略有不同)。如前所述,對象是持久化的,如果是自有對象,需要用戶簽名才能在智能合約調用中使用。當使用鍵能力時,結構的第一個字段必須是具有UID類型的對象ID。這將給它一個全球唯一的ID,能夠用它進行引用。
- 存儲 - 允許將該結構作為一個字段嵌入另一個結構中
- 復制 - 允許從任何地方任意復制/克隆該結構
- 刪除 - 允許從任何地方任意銷毀該結構
從本質上講,Move中的每個結構都是默認的資源。能力給了我們權力,可以精細地放寬這些限制,使其表現得更像傳統結構。
4.2. 幣(Coin)
幣在Sui中實現了類似ERC20/SPL代幣的功能,是Sui Move Library的一部分。它的定義是這樣的:
你可以在Sui代碼庫中找到完整的模塊實現(鏈接)。
幣類型具有鍵和存儲的功能。鍵意味著它可以作為一個對象使用。這允許用戶直接擁有幣(作為一個頂層對象)。當你擁有一個幣時,除你之外,其他人甚至不能在交易中引用它(更不用說使用它)。存儲意味著,幣可以作為一個字段嵌入到另一個結構中,這對于可組合性很有用。
由于沒有丟棄功能,幣不能在函數中被意外丟棄(銷毀)。這是一個非常好的特性——它意味著你不會意外地丟失一個幣。如果你正在實現以接收硬幣為參數的函數,在函數結束時,你需要明確地對它做一些事情——把它轉移給用戶,把它嵌入另一個對象,或者通過調用把它送入另一個函數(同樣需要對它做一些事情)。當然,通過調用幣模塊中的coin::burn函數來銷毀一個幣是可能的,但你需要有目的地這樣做(你不會這樣意外操作的)。
沒有克隆能力意味著沒有人可以復制幣,從而憑空創造新的供應。創造新的供應可以通過coin::mint函數來完成,而且只能由該幣的國庫能力對象(treasury capability)的所有者調用。
另外,由于泛型(generics)的存在,每個不同的硬幣都是獨特類型。由于兩個幣只能通過coin::join函數加在一起(而不是直接訪問它們的字段),這意味著不可能把不同類型的幣值加在一起(幣A+幣B)——因為沒有這種簽名的函數。類型系統能夠保護我們免受壞賬影響。
在Move中,資源的安全性由其類型定義。考慮到Move有全局類型系統,這使編程模型更自然和更安全,資源可以直接傳入和傳出不受信任的代碼。
4.3. 字節碼驗證
如前所述,移動智能合約是作為模塊發布的。任何人都被允許創建并上傳任何任意模塊到區塊鏈上,由任何人執行。我們也已經看到,Move對結構體的使用方式有一定規則。
那么,是什么保證了這些規則被任意模塊所遵守?是什么阻止了人們上傳具有特殊制作字節碼的模塊,例如接收一個幣對象,然后直接改變其內部字段來繞過這些規則?通過這樣做,可以非法地增加所有幣的數量。光是字節碼的語法就肯定允許這樣做。
DFINITY基金會:DFINITY上運行的Canister智能合約已超過 11,300:10月21日消息,官方消息,DFINITY Foundation宣布,DFINITY上運行的Canister智能合約已超過 11,300。Canister智能合約是由周期驅動的計算單元。這一增長說明了網絡上不斷增長的開發者活動。[2021/10/22 20:47:26]
字節碼驗證可有防止這種類型的濫用。Move驗證器是一個靜態分析工具,它分析Move字節碼并確定它是否遵守所需的類型、內存和資源安全規則。所有上傳到鏈上的代碼都需要通過驗證器。當你試圖上傳一個Move模塊到鏈上時,節點和驗證器將首先通過驗證器運行,然后才允許提交。如果任何模塊試圖繞過Move的安全規則,它將被驗證器拒絕,并且不會被發布。
Move字節碼和驗證器是Move的核心創新之處。它實現了一個以資源為中心的直觀編程模型,在其他處是無法實現的。最關鍵的是,它允許結構化類型跨越信任邊界而不失去其完整性。
在Solana上,智能合約是程序,而在Move中,它們是模塊。這似乎只是一個語義上的差異,但事實并非如此,具有重大意義。區別在于,在Solana上,跨程序邊界是沒有類型安全的——每個程序通過手動從原始賬戶數據解碼來加載實例,這需要手動進行關鍵的安全檢查,也沒有本地資源安全。相反,資源安全必須由每個智能合約單獨實現。這確實能夠實現足夠的可編程性,但與Move的模式相比,它在很大程度上阻礙了可組合性和人機工程學,因為Move的模式對資源有原生支持,它們可以安全地流入和流出不信的代碼。
在Move中,類型確實存在于各個模塊中——類型系統是全局的。這意味著不需要CPI調用,賬戶編碼/解碼,賬戶所有權檢查等——你只需直接調用另一個模塊中的函數與參數。整個智能合約的類型和資源安全由編譯/發布時的字節碼驗證來保證,不需要像Solana那樣在智能合約層面上實現,然后在運行時檢查。
現在我們已經看到了Move編程如何工作,根本安全的原因。那么讓我們從可組合性、人機工程學和安全性的角度深入了解一下這對智能合約編程有什么樣的影響。在這里,我將把Move/Sui的開發與EVM和Rust/Solana/Anchor進行比較,以幫助理解Move的編程模型所帶來的好處。
5.1. 閃電貸
閃電貸是DeFi中的一種貸款類型,貸款金額必須在借入的同一交易中償還。這樣做的主要好處是,由于交易是原子性的,貸款可以完全沒有抵押。這可以用來在資產之間進行套利,而不需要有本金。
實現這一目標的主要困難是——你如何從閃電貸智能合約中,保證貸款金額將在同一交易中得到償還?為了使貸款無需抵押,交易需要是原子性的——也就是說,如果貸款金額沒有在同一交易中被償還,整個交易需要失敗。
EVM有動態調度,所以可以使用重入性(reentrancy)來實現這一點,如下所示:
- 閃電貸用戶創建并上傳自定義智能合約,當調用該合約時,將通過調用將控制權傳遞給閃電貸智能合約
- 然后,閃電貸智能合約將向自定義智能合約發送請求的貸款金額,并調用自定義智能合約中的executeOperation()回調函數。
- 然后,自定義智能合約將使用收到的貸款金額來執行它所需要的操作(如套利)。
- 在自定義智能合約完成其操作后,它需要將借出的金額返回給閃電貸智能合約。
- 這樣,自定義智能合約的executionOperation()就完成了,控制權將返回給閃電貸智能合約,它將檢查貸款金額是否已經正確返回。
- 如果自定義智能合約沒有正確返回貸款金額,整個交易將失敗。
這很好地實現了所需的功能,但問題是,它依賴于可重入性,我們非常希望它不要出現在在智能合約編程中。因為可重入性本質上非常危險,是許多漏洞的根本原因,包括臭名昭著的DAO黑客襲擊。
Solana在這方面做得更好,因為它不允許重入。但是,如果沒有可重入性,如果閃電貸款智能合約無法回調到自定義智能合約,如該何在Solana上實現閃電貸款?多虧了指令自省( instruction introspection)。在Solana上,每個交易由多個指令(智能合約調用)組成,從任何指令中你都可以檢查同一交易中存在的其他指令(它們的程序ID、指令數據和賬戶)。這使得實現閃存貸款成為可能,具體如下:
閃電貸款智能合約實現借款(borrow)和還款(repay)指令
用戶通過在同一交易中把借款和還款指令的調用堆迭在一起,創建一個閃電貸交易。借款指令在執行時,將使用指令自省檢查償還指令是否安排在同一交易的后期。如果償還指令的調用不存在或無效,交易將在這個階段失敗。
在借款和還款的調用之間,借來的資金可以被任何其他處于兩者之間的指令任意使用。
在交易結束時,還款指令調用將把資金返還給閃電放款人智能合約 (該指令的存在將在借款指令的反思中進行檢查)
這個解決方案足夠好,但仍不理想。指令自省在某種程度上是一個特例,在Solana中并不常用,它的使用要求開發者掌握大量概念,其實現本身也有很大技術要求,因為有一些細微差別需要適當考慮。還有一個技術上的限——-償還指令需要靜態地存在于交易中,因此不可能在交易執行期間通過CPI調用動態地調用償還。這并不是什么大問題,但在與其他智能合約整合時,它在一定程度上限制了代碼的靈活性,也將更多復雜性推向客戶端。
Move也禁止動態調度和重入,但與Solana不同的是,它有一個非常簡單和自然的閃電貸解決方案。Move的線性類型系統允許創建結構,保證在交易執行過程中正好被消耗一次。這就是所謂的 “燙手山芋”(Hot Potato)模式——一個沒有鍵、存儲、刪除或克隆功能的結構。實現這種模式的模塊通常會有一個實例化結構的函數和一個銷毀結構的函數。由于”燙手山芋”結構沒有丟棄、鍵或存儲功能,因此可以保證它的銷毀(destroy)函數能被調用,以此來消耗它。盡管我們可以將其傳遞給任何模塊中的任何其他函數,但最終它還是需要在銷毀函數結束。因為沒有其他方法來處理它,而且驗證器要求在交易結束時對它進行處理(它不能被任意丟棄,因為沒有丟棄功能)。
讓我們看看如何利用這一點來實現閃電貸。
- 閃電貸智能合約實現了一個“燙手山芋”的收據(Receipt)結構
- 當通過調用貸款函數進行貸款時,它將向調用者發送兩個對象——請求的資金(一個幣)和一個收據,是需要償還貸款金額的記錄。
- 然后,借款人可以將收到的資金用于其需要的操作(如套利)。
- 在借款人完成其預期的操作后,它需要調用還款函數,該函數將收到借款資金和收據作為參數。這個函數被保證在同一個交易中被調用,因為調用者沒有其他辦法擺脫收據實例(它不允許被丟棄或嵌入到另一個對象中,這是驗證器所要求的)。
- 還款函數通過讀取嵌入在收據中的貸款信息來檢查是否已返回正確的金額。
Move的資源安全特性使Move中的閃電貸成為可能,而無需使用重入或自省。它們保證了收據不能被不受信任的代碼所修改,并且它需要在交易結束時被返回給還款函數。這樣,我們可以保證在同一個交易中返回正確的資金數額。
該功能完全使用基本的語言原語實現,Move的實現不會像Solana的實現那樣受到集成問題的影響,因為后者需要交易是精心設置的。沒有任何復雜性被推到客戶端。
閃電貸很好展示Move的線性類型系統和資源安全保障如何使我們以其他編程語言無法實現的方式去表達功能。
Plasm創始人:智能合約已鎖倉15萬枚ETH,正在開發以太坊橋:Plasm創始人Sota Watanbe發推稱:目前已經收到6項Web3基金會捐助,已經提交四項捐贈。智能合約鎖倉15萬枚ETH,社區成員超過3萬人。接下來Plasm將推出以太坊橋、發布Plasm Network Portal以及成為Kusama/波卡平行鏈。[2021/2/4 18:51:20]
5.2. 鑄幣權限鎖(Mint Authority Lock)
“鑄幣權限鎖”智能合約擴展了代幣鑄造的功能,允許多個白名單方(authority)鑄造代幣。該智能合約的所需功能如下(同時適用于Solana和Sui的實現):
- 原始的代幣鑄幣權限方創建一個“鑄幣鎖”,這將使我們的智能合約能夠監管鑄幣。調用者成為該鑄幣鎖的管理員。
- 管理員可以為該鎖創建額外的鑄幣授權,可以授權給其他各方,并允許他隨時使用該鎖來鑄造代幣。
- 每個鑄幣授權都有 每日可以鑄造的代幣數量限制。
- 管理員可以在任何時候禁止(和解除)任何鑄幣權限方。
- 管理員的能力可以轉讓給另一方。
這個智能合約可用于,例如將代幣的鑄幣能力交給其他用戶或智能合約,而原來的鑄幣權限方(管理員)仍然保留對鑄幣的控制權。不然,我們將不得不把鑄幣的全部控制權交給另一方,這并不理想,因為我們只得相信它不會濫用該權力。而且給多方提供許可也是不可能的。
這些智能合約的完整實現可以在這里(Solana)和這里(Sui)找到。
注意:請不要在生產中使用這段代碼! 這是示例代碼,僅用于教育目的。雖然我已經測試了它的功能,但我還沒有做徹底的審計或安全測試。
現在讓我們來看看這些代碼,看看實現方式有什么不同。下面是這個智能合約的完整Solana和Sui實現的并排代碼截圖。
可以注意到的是,對于相同的功能,Solana的實現的規模是Sui的兩倍多(230 LOC vs 104)。這是一個大問題,因為更少代碼通常意味著更少錯誤和更短開發時間。
那么,Solana的這些額外行數是怎么來的呢?如果我們仔細看Solana的代碼,我們可以把它分為兩個部分——指令實現(智能合約邏輯)和賬戶檢查。指令實現與我們在Sui上的情況比較接近-——Solana136行,Sui上104行。額外的行數源于兩個CPI調用的引用(每個大約10個LOC)。最重要的差異是在賬戶檢查(在上面的截圖中標為紅色的部分),這在Solana上是必須的(事實上是關鍵的),但在Move中不是。帳戶檢查占這個智能合約的大約40%(91 LOC)。
Move不需要賬戶檢查。LOC的減少能夠帶來利處,但同時去除做賬戶檢查也十分必要。因為事實證明,正確實施這些檢查是非常棘手的,如果你在犯了哪怕一個錯誤,往往會導致重大漏洞和用戶資金的損失。事實上,一些最大的(就用戶資金損失而言)Solana智能合約漏洞就是由不當的賬戶檢查引起的賬戶替換攻擊。
- Wormhole(3.36億美元) - https://rekt.news/wormhole-rekt/
- Cashio (4800萬美元) - https://rekt.news/cashio-rekt/
- Crema Finance (880萬美元) - https://rekt.news/crema-finance-rekt/
那么,Move是如何做到沒有這些檢查又同樣安全的呢?讓我們仔細看看這些檢查的實際作用。這里是mint_to指令所需的賬戶檢查(權限持有人通過調用這個指令來鑄造代幣):
有6個檢查(用紅色標出):
1. 檢查所提供的鎖賬戶是否為該智能合約所擁有,并且是MintLock類型的。需要傳入鎖,因為要用于CPI調用,到代幣程序進行鑄幣(它存儲了權限)。
2.檢查所提供的鑄幣權限賬戶是否屬于所提供的鎖。鑄幣權限賬戶持有權限狀態(它的公鑰,它是否被禁止,等等)。
3.檢查指令調用者是否擁有該權限的所需密鑰(所需權威簽署了該交易)。
4.需要傳入代幣目標賬戶,因為代幣程序將在CPI調用中更改它(增加余額)。鑄幣檢查在此處并不是嚴格必要的,因為如果傳入了錯誤賬戶,CPI調用就會失敗,但這個檢查還是很好的做法。
5.與4類似。
6.檢查代幣程序賬戶是否被正確傳入。
? 我們可以看到,賬戶檢查(在這個例子中)分為這五類:
? - 帳戶所有權檢查(1,2,4,5)
? - 帳戶類型檢查(1、2、4、5)
? - 帳戶實例檢查 (某一賬戶類型的正確實例是否被傳入)(2,5)
? - 賬戶簽名檢查 (3)
? - 程序賬戶地址檢查 (6)
但在Move中,沒有賬戶檢查或類似的東西,只有功能簽名:
mint_balance函數只需要四個參數。在這四個參數中,只有lock和cap代表對象(有點類似于賬戶)。
在Solana中,我們需要聲明6個賬戶,并手動實現對它們的各種檢查,而在Move中,我們只需要傳入2個對象,而且不需要明確的檢查,這是如何實現的?
在Move中,這些檢查有些是由運行透明地完成的,有些是由驗證器在編譯時靜態地完成的,而有些則是在構造上根本不需要的。
賬戶所有權檢查——Move有類型系統,因此這種設計不必要。一個Move結構只能通過其模塊中定義的函數進行改動,而不能直接改動。字節碼驗證保證了結構實例可以自由地流入不受信任的代碼(其他模塊)而不被非法改動。
賬戶類型檢查——沒有必要,因為Move類型存在于整個智能合約中。類型定義被嵌入到模塊二進制文件中(在區塊鏈上發布并由虛擬機執行)。驗證器將檢查,編譯/發布期間,我們的函數被調用時,正確的類型是否被傳遞。
賬戶實例檢查——在Move中(有時在Solana上也是如此),你會在函數主體中做這件事。在這個特例中,這是沒有必要的,因為鎖和cap參數類型的通用類型參數T強制要求對cap(鑄幣能力/權限)對象的傳入要正確匹配其鎖(每個幣類型T只能有一個鎖)。
帳戶簽名檢查——我們在Sui中不直接處理簽名。對象可以由用戶擁有。造幣權限由造幣權限能力對象的所有權授予(由管理員創建)。在mint_balance函數中傳遞對該對象的引用將允許我們進行鑄幣。自有對象只能由其所有者在交易中使用。換句話說,對象的簽名檢查是由運行透明地完成的。
從本質上講,Move利用字節碼驗證,以使數字資產的編程模型更加自然。Solana的模型圍繞賬戶所有權、簽名、CPI調用、PDA等。但我們退一步想一想,就會發現,我們并不想處理這些問題。它們與數字資產本身沒有任何關系——相反,我們不得不使用它們,因為這使我們能夠在Solana的編程模型中實現所需功能。
獨家 | 區塊鏈項目10%-20%的智能合約業務存在邏輯漏洞:多家交易所為了保護各平臺投資者及自身的資產安全,委托獨立第三方智能合約審計機構降維安全實驗室對所有即將在交易所上線的項目方智能合約進行安全審計。在審計過的數百份智能合約中,降維安全實驗室(Johnwick.io)發現超過70%的智能合約代碼優質、業務邏輯嚴謹,但也有極少數項目方合約代碼存在嚴重的業務邏輯風險,上線后可能會給交易平臺及投資者造成巨大的資產損失。主要問題表現為合約Owner(合約所有者、創始團隊)權限設置過大,可以無限增發代幣,更為嚴重的是溢出可以隨意增加和銷毀任意地址的代幣,甚至包含交易所自身的錢包地址。在這種情況下如果Owner自身作惡或者被盜取賬號,在上線交易拉高幣價后進行大量增發再砸盤至價格歸0,可在短期內獲得巨大收益,而蒙受巨大損失的只有交易所及廣大投資者。降維安全實驗室作為合約審計的受委托方,在審計合約時以最大程度的保護廣大區塊鏈投資者及交易平臺的資產為唯一目標,對合約有極高的要求所以可能會出現審計不通過的情況,對于沒有經過嚴格評估過的合約資產,可能在未來的某段時間內爆發資產風險,希望廣大用戶注意。[2018/7/12]
在Solana上,沒有字節碼驗證來保證更細化的類型或資源安全,你不能允許任何程序改動任何賬戶,所以引入賬戶所有權的概念是必要的。由于類似原因(沒有跨程序調用的類型/資源安全),也沒有可以進出程序的用戶所有對象的概念,相反,我們用賬戶簽名來證明權限。由于有時程序也需要能夠提供賬戶簽名,所以我們有PDA......
雖然你可以在Solana上擁有與Move相同的跨程序類型和資源安全,但你必須使用低級別的構建模塊(賬戶簽名、PDA...)手動實現它。歸根結底,我們正在用低級別的基元來構建可編程的資源(線性類型)。而這就是賬戶檢查的作用——它們是實現類型安全和手動建模資源需進行的開支。
Move對資源進行原生的抽象,允許我們直接處理資源,而不需要引入任何低級的構建塊,如PDA。跨越智能合約邊界的類型和資源安全保障是由驗證者確保的,不需要手動實現。
5.3 Solana可組合性的局限性
我想再舉一個例子,強調Solana上智能合約可合成性的一些痛點。
我們在鑄幣權限鎖的例子中看到,與Sui相比,我們需要在Solana上聲明更多的輸入(Solana上的6個賬戶 vs. Sui上的2個對象的mint_to調用)。顯然,處理6個賬戶比處理2個對象更麻煩,特別是如果考慮到還需要為賬戶實現賬戶檢查。理論上來說這部分是可控的,但當我們開始在單一的調用中把多個不同智能合約組合在一起時會發生什么?
假設我們想創建一個智能合約,能夠做以下事情:
它從鑄幣權限鎖程序中擁有某個代幣的鑄幣權,可以進行鑄幣
當它被調用時,它將使用其權限來鑄造用戶指定數量的代幣,使用AMM將其交換為不同的代幣,并在同一指令中將其發送給用戶
這個例子的重點是為說明鑄幣權限鎖智能合約和AMM智能合約將如何被組合在一起。指令調用的賬戶檢查可能看起來像這樣:
17個賬戶。每個CPI調用(鑄幣和交換)5-6程序,加上程序賬戶。
在Sui上,一個相當的函數的簽名是這樣的:
只有3個對象。
為什么我們在Sui上傳遞的對象與Solana上的賬戶相比要少得多(3比17)?從根本上說,是因為在Move中我們能夠嵌入(包裹)它們。類型系統的安全保障使我們能夠做到這一點。
下面是一個Solana賬戶和Sui對象之間的比較,它們持有一個AMM池的狀態。
我們可以看到,在Solana上我們存儲了其他賬戶的地址(Pubkeys),它們就像指針一樣,并不存儲實際的數據。為了訪問這些賬戶,它們需要被單獨傳入,我們還需手動檢查正確的賬戶是否被傳入。在Move中,我們能夠將結構相互嵌入并直接訪問其值。我們可以混合和匹配來自任何模塊的類型,同時它們保留其資源和類型的安全保證,這都得益于Move的全局類型系統和資源安全,它們都由字節碼驗證所驅動。
但是,在組成多個智能合約時,不得不傳遞(并因此檢查)許多賬戶,這造成了相當大的實施復雜性,并具有安全影響。這些賬戶之間的關系可能相當錯綜復雜,在某種程度上,難以跟蹤所有必要的賬戶檢查及其是否正確實施。
其實,這就是我認為在Cashio漏洞中發生的情況(4800萬美元)。下面是該(不充分)賬戶檢查的分解,也由此導致了該漏洞。如你所見,這些賬戶檢查變得有些復雜。開發者充滿好的意圖進行正確檢查,但在某某程度上,精神壓力變得太大,就會非常容易出錯。賬戶越多,越容易出現錯誤。
Move的全局類型系統和更自然的編程模型,意味著我們可以在達到心理承受壓力的極限之前,以更大的安全性推動智能合約的構成。
附帶說明一下,Move的TCB(可信計算基礎)要比Rust/Anchor小得多。較小的TCB意味著需要進入智能合約編譯執行、被信任的的組件較少。這就減少了可能影響智能合約的漏洞表面積——TCB之外的漏洞不會影響智能合約的安全。
Move的設計考慮到了減少TCB——為盡可能減少TCB,Move做了許多決定。字節碼驗證器將許多由Move編譯器執行的檢查從TCB中移除,而在Rust/Anchor中,有更多的組件需要被信任,因此致命安全錯誤的表面積要更大。
我們能否在Solana上擁有Move,以及如何擁有?
6.1. 有全局類型安全的Anchor?
在我們開始研究之前,讓我們簡單看看Anchor,并做個小的思想實驗。也許我們可以以某種方式升級Anchor,來提供我們從Move中得到的一些好處?也許我們可以獲得對跨程序調用的類型安全的本地支持?畢竟,Anchor指令已經類似于Move的入口函數:
也許我們可以延伸Anchor,使賬戶能直接被傳入指令參數。
我們可以避免賬戶檢查?
在這種情況下,我們希望類型檢查由運行而不是程序來完成——運行將讀取Anchor賬戶判別器(或其等價物),并能夠檢查賬戶傳入是否符合所需的判別器(Anchor賬戶的前8個字節)。
Solana不對同一程序的不同指令調用進行區分,這是由程序手動實現的(在這種情況下,繁重的工作由Anchor完成)。因此,為了做到這一點,運行必須以某種方式了解不同指令、它們的簽名、類型信息。
Solana程序編譯為SBF(Solana Bytecode Format,eBPF的一種變體),并以這種方式上傳到鏈上(和執行)。SBF本身并沒有嵌入任何可以幫助我們的類型或函數信息。但也許我們可以修改SBF,以允許指令和類型信息被嵌入二進制文件中?這樣所需的指令和簽名信息就可以由運行從二進制文件中讀取。
智能合約先行者Nick Szabo:不要放棄去中心化:在上周四于紐約舉行的會議上,Nick Szabo,1996年首次提出開發智能合約概念的密碼學專家,他敦促觀眾不要忽視區塊鏈最初的承諾。“我只是想強調信任最小化和去中心化的好處,”他在參與智能合同小組討論時說, “傳統的銀行模式是‘我們相信自己,你為什么不相信我們?'”他繼續說道,認為客戶可能不信任銀行,“他們甚至可能會恨你。”[2018/4/22]
我們確實可以這樣做。這將要求相當大的工程量,特別是考慮到我們需要保持與舊程序的向后兼容,但這是我們能獲得的好處:
-賬戶所有權和類型檢查由運行而不是程序完成
-對于在編譯時已知地址的賬戶(例如程序賬戶),我們可以避免從客戶端傳入它們,現在可以由運行傳入。
-如果我們設法將賬戶約束嵌入到二進制文件中,我們可以進一步減少必須由客戶端傳入的賬戶數量,用運行對其進行動態遞歸在加載(基于嵌入的約束信息)。
我們仍然沒有得到:
-嵌入的賬戶。我們仍然必須使用Pubkeys來引用其他賬戶,而不能夠直接嵌入它們。這意味著我們沒有擺脫第5.3節中描述的賬戶臃腫的問題。
-當進行跨程序調用時,賬戶類型檢查仍然需要在運行時動態進行,而不是像Move中那樣在編譯時靜態進行。
注意:這只是一個思想實驗。并不說明其可以安全完成,也不是代表其實現困難,更不標榜這些好處值得付出工程量般的努力。
這些好處確實不錯,但從智能合約開發的角度來看,它們并沒有從根本上改變什么。在運行時而不是程序中做類型檢查可能能帶來一些性能上的好處,而且不必在編譯時從客戶端手動傳遞地址賬戶,在一定程度上提升工效(這也可以通過工具化來緩解)。但我們最終仍然在處理Solana的編程模型,它本身在處理數字資產上提供更多幫助——我們仍然沒有原生的資源安全,我們不能嵌入賬戶,所以仍然有賬戶膨脹問題,我們仍然在處理賬戶簽名和PDA......
理想情況下,我們希望所有的智能合約都生活在一個單一的類型系統中,并且能夠像Move那樣自由地將對象傳入傳出。但由于其他智能合約不能被信任,我們不能直接這樣做。為了繞過這一點,Solana設有程序分離和賬戶所有權——每個程序管理自己的賬戶,它們通過CPI調用進行互動。這很安全,并允許足夠的可編程性,但由此產生的編程模型并不理想——沒有全局類型系統,也就沒有有實質意義的資源安全。
我們希望有一個自然的編程模型,但與此同時,我們也在處理不受信任的代碼。雖然在Solana上我們可以安全地處理不受信代碼,但在編程模型上上做出妥協。字節碼驗證使我們有可能同時擁有兩者。沒有它,我們似乎真的無法改善編程模型。
6.2 Solana字節碼格式
如前所述,SBF(Solana字節碼格式),即Solana智能合約的編譯和鏈上存儲格式,是基于eBPF的。在Solana上使用eBPF而不是任何其他字節碼格式(如WASM),主要是因為Solana對安全和高性能智能合約執行的要求,與eBPF設計的內核沙盒程序執行要求一致(它也需要安全和高性能)
從表面上看,eBPF確實是一個可靠的選擇。高性能、圍繞安全設計,程序的大小和指令的數量是有限的,有一個字節碼驗證器......看起來很有不錯。
但讓我們看看這在實踐中意味著什么。也許我們可以以某種方式利用eBPF驗證器來提高我們智能合約的安全性?以下是eBPF驗證器所做的一些事情:
-不允許無限循環
-檢查程序是否是一個DAG(有向無環圖)
-不允許越界跳轉(out-of-bounds jump)
-在進行各種輔助(helper)函數調用時檢查參數類型(輔助函數在內核中進行定義,例如用于修改網絡數據包)。
好吧,禁止越界跳轉似乎很有用,但其他作用有限。事實上,強制要求程序必須是一個DAG并且沒有無限循環是有問題的,因為它大大限制了程序的可操作性(我們沒有圖靈完備性)。在eBPF程序中需要這樣做的原因是,驗證器需要確定程序在一定數量的指令內終止(這樣程序就不會使內核終止;這就是著名的停機問題),而氣體計量(gas metering)不是一個選項,因為它將太過影響性能。
雖然這種取舍對實現高性能的防火墻來說是很好的,但對于智能合約的開發來說就不那么好了。eBPF驗證器的絕大部分都不能被重用在Solana程序上。事實上,Solana根本就沒有使用原始的eBPF驗證器,它使用的是一個(更基本的)自定義驗證器,主要是檢查指令是否正確和是否有越界跳轉。
同時,eBPF在設計上最多允許5個參數被傳遞給一個函數進行調用。這意味著Rust標準庫不能直接編譯到eBPF。或棧的大小被限制在512字節,這減少了我們可以傳遞給一個函數的參數的大小而不需要堆分配(heap allocation)。
因此,即使Rust編譯到LLVM,有LLVM的eBPF后端,甚至支持Rust編譯器針對eBPF使用,你仍然無法使Solana智能合約以其本來的樣子編譯到eBPF上。這就是為什么Solana團隊不得不對Rust代碼庫和eBPF LLVM后端(例如,通過棧傳遞參數)進行多次修改。
由于其中一些修改本身是支持上游(無論是Rust還是LLVM),所以Solana團隊目前在維護Rust和LLVM的分叉時都做了這些修改。當你執行cargo build-bpf(構建Solana智能合約的典型命令)時,Cargo會拉出這個Solana特定版本的rustc(Rust編程語言的編譯器)來進行智能合約的編譯(原來的rustc不起作用)。
這就是SBF的誕生過程——Solana需要的一些要求與eBPF不兼容。Solana團隊目前正在努力將SBF作為一個獨立的LLVM后端上流,并將其作為一個Rust目標加入,以避免維護單獨分叉。
因此,雖然eBPF可以作為智能合約的一種格式,但它并不像表上看起來那么理想。它需要進行一些修改,而且原來的驗證器也沒有很大的用處。
在關于Move和Solana/SBF的討論中,一個誤解就是,一些人認為Move的主要思想應該適用于SBF,因為它是基于eBPF的,也許可以利用其驗證器做靜態的賬戶改動檢查,而不是在運行時做動態檢查。
在我看來,這是一個令人懷疑的說法。即使有可能證明,程序不會在eBPF中改動他們不擁有的賬戶,這也確實是Move在做的事情,但這肯定不是Move的主要想法。
Move的主要思想是創造一個以資源為中心的編程模型,能夠自然地與不可信代碼互動。
在實踐中,這意味著:
- 全局類型安全
- 資源安全(鍵、克隆、存儲、丟棄)
- 可嵌入的資源
- 資源安全地流入和流出不受信任的代碼
...
將主要的Move思想引入eBPF/SBF非常難。如果不對eBPF進行重大修改,強制執行一些特性比如“這個不受信任的代碼不能丟棄一個T”是不可能的。這需要大量修改,以至于你最終會得到一個新的字節碼,它看起來更像Move而不是eBPF。
事實上,類似的思路是導致Move誕生的首要原因。Move團隊(當時在Diem)最初考慮從其他格式出發,如WASM、JVM或CLR,但事后添加這個實在是太難了——線性/能力是非常規的。所以Move是從頭開始設計的,其想法是通過輕量級的驗證器通道來有效執行這些檢查。
如果你仔細想想,這其實并不令人驚訝。畢竟最終,智能合約編程不是系統編程,后端編程,或任何一種其他傳統編程,它是一種完全不同的編程類型。所以現有字節碼和指令格式的功能不能被利用也就不足為奇了,因為它們在設計時考慮的是完全不同的使用情況。
我不是在批評Solana使用eBPF。事實上,我認為這是一個非常可靠的選擇,也是團隊考慮到背景的良好判斷。事后來看,團隊可能會選擇WASM而不是eBPF,這樣就可以避免前面提到的將智能合約編譯成eBPF的問題,因為WASM在Rust中有一流的支持(不過WASM可能會有其他問題),但可以看到,考慮到對性能的強調,團隊可能覺得eBPF是一個更安全的選擇。另外,在做出這些設計選擇的時候,Move甚至還沒有進行宣布,對于一個初創公司來說,從頭開始創建一種新語言肯定不是一個合理的選擇。最終,Solana設法提供了一個成功的高性能L1,這才是最重要的。
有三種方法可以在Solana上獲得Move:
- 將Move虛擬機作為一個本地加載器添加(與SBF虛擬機一起)
- 將Move虛擬機作為一個程序運行(如Neon)
- 將Move編譯為SBF(像Solang)
讓我們先來討論(3)。這里的想法是為Move建立一個LLVM前端,以便將其編譯為SBF。編譯成SBF的Move智能合約被透明地執行,就像用Rust(或其他任何可以編譯成SBF的語言)構建的智能合約一樣,而且運行時不需要對Move有任何區分或了解。從運行角度來看,這將是一個非常優雅的解決方案,因為它不需要改變它或它的安全假設。
但我認為以這種方式開發智能合約會比直接使用Anchor更糟。你通過(3)得到的是Solana編程模型中的Move語法。這意味著第五章中討論的Move的所有重要優勢(全局類型安全、全局資源安全、可嵌入對象......)將不復存在。相反,我們仍將不得不處理賬戶檢查、CPI調用、PDA等問題,就像在Rust中一樣。而且,由于Move不支持宏(macro),因此使用eDSL實現一個像Anchor這樣的框架,來簡化其中的一些工作是不可能的,所以代碼將與原始Rust相似(但可能更糟糕)。Rust標準庫和生態系統也是不可用的,所以像賬戶序列化和反序列化這樣的事情必須在Move中重新實現。
Move不是很適合與其他編程模型一起使用。這是因為它被特別設計為能夠編譯成Move字節碼,并通過驗證器。考慮到圍繞能力和借貸檢查器的自定義規則,這是必要的。其字節碼驗證十分特殊具體,以至于其他語言幾乎沒有機會編譯成Move字節碼并通過驗證器。因為Move圍繞這種非常特殊的字節碼驗證而設,所以它不像Rust等語言那樣靈活。
剝離字節碼就放棄了Move的所有主要優勢。雖然Move的類型、資源和內存安全特性會在程序級別上被保留,但它們不會被全局保留。而程序級的安全并沒有帶來多少新的結果——通過Rust我們已經實現了這些結果。
Move的智能合約生態系統也不能在Solana上使用——編程模型不同,以至于智能合約的重要部分必須被重寫。考慮到所有這些,我預計用(3)實現Move的做法不會被接受。
至于(1),這里的想法是(與SBF加載器一起)在運行時添加對Move加載器的支持。Move智能合約將被存儲為鏈上的Move字節碼,并由Move VM執行(就像在Sui中一樣)。這意味著我們將有一個SBF智能合約的生態系統和一個Move智能合約的生態系統,前者將在當前的Solana編程模型上運行,而后者則在一個(可以說是更高級的)Move模型上運行。
有了這種方法,就有可能保持Move智能合約之間相互作用的所有好處,但這里的一個挑戰是讓Move智能合約能夠與SBF智能合約進行互動,反之亦然——你需要一個對Move和Solana有深刻理解的人,驗證器也必須進行調整。
還有一個缺點是需要在運行時維護兩個不同的加載器。這會對安全有影響,因為它意味著攻擊面會翻倍——任何一個加載器的錯誤都可能意味著整個鏈被利用。實際上早在2019年,Solana就加入了對MoveVM的早期支持(#5150),但后來由于安全問題而被移除(#11184)。
至于(2),想法是將整個Move VM作為一個Solana程序(智能合約)運行。Move VM是用Rust實現的,所以可能會把它編譯成SBF(除非它使用線程或其他不支持的API)。雖然這聽起來很瘋狂,但Neon已經實現了類似的方法,將EVM作為一個Solana程序來運行。這種方法的好處是,不需要對運行進行修改,而且可以保持相同的安全假設。
我不熟悉Move VM的技術細節,所以我不能對這種做法的可行性以及它的局限性做太多評論。我的第一個反應是,驗證器也必須作為一個程序運行,這意味著在計算預算內。這種方法也會像(1)一樣,受到SBF和Move智能合約之間互操作性問題的影響。
沒有直接的方法可以將Move的主要功能帶到Solana。雖然有可能建立一個LLVM前端,并將Move編譯為SBF,但這不會起太多作用,因為編程模型將保持不變。正如第6.1節中的思想實驗所說明的那樣,如果沒有某種字節碼驗證,就無法改善編程模型。改變eBPF/SBF以支持字節碼驗證將是非常困難的。似乎唯一合理的選擇就是以某種方式讓MoveVM運行。但這意味著將有兩個生態系統在不同的編程模型上運行,而讓它們正確地互操作是極具挑戰性。
6.4. Move的性能
Move的字節碼不是一種通用的字節碼語言。它有一個非常有“主見”的類型系統,為允許所有必要驗證,它是相當高級的。這意味著與其他字節碼格式(如eBPF/SBF)相比,其性能較低,因為后者更接近于本地代碼,人們可能會認為這對于其在高性能L1中的使用是一個問題。
但是,到目前為止,智能合約的執行在Solana(在寫這篇文章的時候,平均有3k TPS)和Sui(基于團隊所做的最初e2e基準)上都還未成為瓶頸。提高交易處理性能的主要方式就是并行執行。Solana和Sui都實現了這一點,它們要求事先聲明依賴關系,并對依賴不同對象/賬戶集的事務執行進行并行調度。
此外,一旦TX執行出現在關鍵路徑上,沒有任何東西可以阻止Move被AOT編譯或JIT化以提高性能。這就是為Move構建一個LLVM前端的好處所在。另外,由于Move本身對靜態分析的適應性,Move也可能取得特有的進一步優化。
考慮到所有這些,我希望Move的性能在可預見的未來不會成為一個重要的障礙。
7.1. 驗證器
Move有一個用于智能合約的形式化驗證工具,叫做Move Prover。通過這個工具,你能夠判斷不同的不變量對你的智能合約是否成立。在幕后,驗證條件被翻譯成SMT公式,然后使用SMT求解器進行檢查。這與模糊測試有很大不同,例如,模糊測試是通過走查輸入空間來試錯。例如,如果模糊測試和單元/集成測試未能測試出特定的輸入或輸入組合,顯示程序有誤,那么它們仍然可以提供一個假陽性。另一方面,驗證器本質上提供了形式上的證明,即指定的不變量對所提供的程序成立。這就像針對所有可能的輸入檢查程序一樣,但不需要這樣做。
移動驗證器的速度非常快,使它可以像類型檢查器或linter那樣被整合到常規開發工作流程中。
下面是一個驗證器的例子(摘自《用Move Prover對智能合約進行快速可靠的形式驗證白皮書》)。
7.2. 錢包安全
由于Sui要求所有交易將訪問的對象都在函數參數中傳遞(不從全局狀態中動態加載),并且移動函數簽名連同類型信息都存儲在字節碼本身中,我們可以讓錢包在用戶簽名之前向用戶提供更有意義的信息,說明交易的內容。
例如,如果我們有一個具有以下簽名的函數:
我們可以從函數的簽名中看出,這個交易將訪問用戶的3個資產(資產類型)。不僅如此,根據&和&mut關鍵字(或沒有關鍵字),我們還可以知道資產1可以被讀取,資產2可以被改動(但不能轉移或銷毀),而資產3有可能被改動、轉移或銷毀。
錢包可以向用戶顯示這些信息,然后用戶可以更加了解交易可能對資產做出什么動作。如果有什么異樣,例如,來自Web3應用程序的交易調用正在接觸一些不應接觸的資產或幣,用戶可以觀察到這一點,決定不繼續進行交易。
錢包也可以另外模擬交易,這將給用戶提供更多關于其結果的信息。Sui編程模型以對象為中心,類型信息對運行原生,這意味無需對智能合約有任何具體的應用級知識,就能解釋對象的變化。
這在Solana上是不可能的,因為從運行的角度來看,賬戶包含任意數據。你需要賬戶的外部描述(特定于應用程序)才能對其進行解釋,而智能合約發布者未必提供這些信息。另外,Solana運行時中不存在資產所有權的概念,每個智能合約都需要手動實現這一語義(通常使用賬戶簽名和PDA),這意味著沒有通用方法來對此進行追蹤。
7.3 簡單交易和復雜交易
具體到Sui,在共識層面上有一個有趣的優化,允許某些類型的交易放棄完全的共識,轉而使用基于拜占庭一致廣播(Byzantine Consistent Broadcast)的更簡單算法。這樣的好處是,這些交易可以在共識層面上并行,消除隊頭阻塞(head-of-line blocking),達成幾近即時的最終性——基本上實現了web2的可擴展性。
這是由于Sui對自有和共享對象的區分(見3.1節)。只涉及自有對象的交易(被稱為簡單交易)不需要在Sui上達成完全共識。由于自有對象除了發送者外不能在交易中使用,且發送者一次只能發送一個交易,這本身就意味著這些交易不需參照其他交易進行排序(總排序與因果排序)——我們知道交易中引用的對象不能被其他交易影響,且該交易也不能影響其他對象。因此,我們并不關心該事務相對于鏈上平行發生的其他事務的排序——這實際上是不相關的。Sui能夠利用這一事實,大大優化簡單事務的處理,在幾百毫秒內實現最終性。但缺點是,發送者一次只能發送一個交易。另一方面,涉及任何數量共享對象的交易(被稱為復雜交易),總是需要完全共識。
考慮到自有對象的創建、轉移和修改可以完全通過簡單事務完成,某些類型的應用可以很好地利用簡單事務。很好的例子是NFT(包括大規模造幣)和web3游戲。這些用例從低延遲的最終性和消除對頭阻塞中獲益良多,實現了更好的用戶體驗和可擴展性。
但其他類型的應用程序必須依賴復雜交易。這包括大多數DeFi應用程序。例如,AMM流動性池需要成為一個共享對象,因為任何種類的交易所訂單執行都需完全共識和總排序。因為從根本上說,如果多個訂單同時來自不同用戶,我們需要就先執行誰的訂單達成一致,這就決定了每個用戶會得到什么樣的執行價格。
還有一些應用程序可以混合使用簡單和復雜交易。這些應用需要復雜交易才能實現它們所需功能,但在某些操作上可以利用簡單交易來獲得更好效率。例如,一個價格預言機就可以如此設計。我們可以讓多個發布者使用簡單交易,為市場提交價格數據,然后由一個權威機構使用復雜交易對價格進行匯總(例如,股權加權中值)。在某些時候不依靠復雜交易是不可能實現價格預言機的(根本原因是在其他交易中使用發布價格需要就排序達成一致,從而達成完全共識),但至少我們可以用簡單交易優化發布者的寫入。
Sui文檔有關于簡單和復雜交易的更多細節。
https://docs.sui.io/devnet/learn/sui-compared
https://docs.sui.io/devnet/learn/how-sui-works#system-overview
本文深入探討并比較Solana和Sui的編程模型,也對Move編程語言進行探討,
第二章是對Solana編程模型的總結,而第三章則介紹了Sui Move及其編程模型。第4章接著解釋了Move中的類型和資源安全如何運作。Move功能對智能合約開發的意義無法立竿見影,所以在第5章中,我利用現實生活中的例子對Solana和SuiMove進行了更徹底的比較。第6章討論了eBPF/SBF,表明讓Move功能或Move本身在Solana上工作并不容易。第7章討論了Sui的一些Move相關功能。
智能合約編程是關于數字資產的編程。可以說這是一種新的編程類型,與我們目前看到的其他類型編程(如系統、后臺......)截然不同。正因如此,現有編程語言和編程模型自然不能很好適應這種用例。
問題的關鍵在于,我們希望有一個編程模型,能夠自然地與資源打交道,但同時又與不受信的代碼互動。Solana在這里做了妥協,它使智能合約在一個不信任環境中具備了必要的可編程性,但其編程模型對于用資源編程來說并不自然。字節碼驗證使其有可能同時擁有這兩種特性。在某種程度上,它把不受信代碼變成了受信代碼。
Move是一種用于智能合約開發的新型編程語言。它的核心創新之處在于它的字節碼,被特意設計為可被驗證。雖然字節碼驗證本身并不是一個新概念,但Move所做的驗證確實是一種創新。通過其字節碼和驗證,Move實現了一個智能合約編程模型,對資源能夠有一流支持,并能保證在一個不受信任的環境中安全編程。
我認為Move對智能合約開發的作用就像React對前端開發的作用一樣。說“用Move做的事能用Rust做”就像說“用React做的事能用jQuery做”一樣。當然有可能實現基于jQuery的應用,能夠與React應用相當,但這并不實際。React引入了虛擬DOM的概念,這對開發者來說是完全易懂的的,但使前臺的開發速度更快、可擴展、更簡單。同樣,Move的字節碼驗證是一種底層技術,對開發者來說也易于理解,但它提供了一個更符合人體工效學、可組合、更安全的智能合約開發。由于其安全性和更直觀的編程模型,Move也大大降低了智能合約開發者的準入門檻。
如果Move能設法獲得影響(有早期跡象表明它會的),它可能對Solana構成極大威脅。有兩點原因。
首先Move智能合約的開發時間要快得多。在Move中從頭開始開發一個智能合約可能比在Rust中快2-5倍。因此,Move生態系統的發展可以超過Solana。由于區塊鏈的開放性和無許可性,不存在嚴重的鎖定效應,流動性可以輕松移動。Solana的開發者可能純粹因為經濟考量而被迫采用Move——要么轉到Move,要么被Move的開發者超越,因為他們能更快開發出更安全的智能合約。如果你要雇傭一個智能合約開發者,你可以雇傭一個Rust開發者,能建立一個智能合約,或者雇傭一個Move開發者,能在同樣時間內建立兩個更安全的智能合約。這類似于React對前端開發的影響。
第二,Move的入門門檻比Rust或Solidity低得多。因為Move語法更簡單,編程模型更直觀。一些開發人員無法用Rust或Solidity進行智能合約開發,但在Move中可能能夠進行。由于需要學習的概念較少,非智能合約開發者進入Move,要比進入Rust(Rust本身就是一種復雜的語言,再加上Solana的概念,如PDA,會給初學者帶來很多困惑)或Solidity(你需要熟悉語言中非常精細的細節,如重入,以便能夠開發安全的智能合約)容易得多。即使現有Solana和Solidity開發者不轉向Move,尚未進入該領域的開發者市場也比該領域現有的開發者數量多出好幾個量級。由于Move的準入門檻較低,且開發速度更快,它比Rust或Solidity有更好的產品市場適應性,可以從這塊蛋糕中分得更大一杯羹。如果新的開發者開始大量涌入,我希望他們從Move開始,而不是Rust或Solidity。這也類似于React在網絡行業的情況。
正因如此,我完全可以預料,在中長期內,Solana會加入對Move進行一流支持。但這并容易。為了獲得Move的主要好處,Move字節碼需要得到本地支持,這意味著簡單地將Move編譯成eBPF/SBF是不可能的(見第6.3節)。為了保持現有生態系統,兩種運行都需要得到支持。主要的技術挑戰是如何在運行之間實現適當的互操。這需要對Move和Solana的深入了解,所以我希望Solana團隊能在Move團隊的支持下對此進行直接推動。
Move起源于Meta(née Facebook)的Diem項目。Move團隊由Sam Blackshear領導,其任務是弄清楚如何處理智能合約。在仔細研究了這個問題后,他們發現智能合約的編程都是關于數字資產(資源),但現有語言都不支持該用例,于是決定從頭開始建立一種新的編程語言。
我想強調的是,創建一門新語言并不是突然做出的決定,它需要多年的努力才能落地,因為在大多數情況下,使用現有解決方案會更好。Move團隊正確預見到,一種安全、對資源有一流支持、同時又足夠靈活的智能合約語言是可以建立的,僅此一點就顯示出他們高度的專業性。這是團隊和支持該項目的Novi/Meta領導層的一個大膽舉動(會有一些董事和副總裁參與)。Meta后來停止了他們在Diem上的努力,并最終沒能夠收獲其在Move上的投資成果。但它為更廣泛加密貨幣社區做出了偉大貢獻。
總而言之,Move是一項了不起的技術,我相信它會對我們如何開發智能合約產生巨大影響。
鏈捕手
媒體專欄
閱讀更多
DeFi之道
財經法學
成都鏈安
金色早8點
Bress
PANews
Odaily星球日報
Aug. 2022, Thiago FreitasData Source:Art Blocks DashboardArt Blocks 是一個 NFT 平臺.
1900/1/1 0:00:00羅馬不是一日建成的,元宇宙也一樣。面對鋪面而來的元宇宙概念,很多人還一頭霧水。如果我們把視野拔高,以近40年時間軸的方式來看,或許能把元宇宙看得更清楚.
1900/1/1 0:00:00撰文:周舟 來源:虎嗅 APP 「我們要制定 Web3(基于區塊鏈的下一代互聯網)的國際規則,不然幾十年后子孫輩們就會像現在(芯片、操作系統等)一樣被人掐脖子.
1900/1/1 0:00:00原文作者:Naly,由 DeFi 之道翻譯編輯。Cosmos 生態系統正在蓬勃發展。 ATOM 2.0 即將到來。 快來發現你需要知道的一切.
1900/1/1 0:00:00在此次市場周期中,Web3 風投已經演變成一個復雜而且彼此差異化的行業,有傳統的主題驅動型基金、企業風險投資(CVC)、也有新式的DAO風險投資、以及世界頭部交易所旗下的風險投資部門.
1900/1/1 0:00:00原文標題:《06)Think about Web3:我們為什么需要 DAO 操作系統?》撰文:VION WILLIAMS本篇文章的寫作背景是與 Jolestar 在一次線上會議中迸發出的靈感.
1900/1/1 0:00:00