Gavin 的 Web3 理想國:Polkadot 智能合約與虛擬機演進史
作者:國子
波卡,⽼牌區塊鏈項⽬,從事 Web3 ⾏業的⼩夥伴們即使沒⽤過他們的產品,⾄少也知道這麼個項⽬。

近期波卡新聞不斷,新的 JAM 架構讓⼈眼前⼀亮,創始⼈演示 DOOM 遊戲讓我們看到了波卡新的局⾯,讓我對其產⽣了巨⼤的研究興趣,⽽波卡項⽬挺龐⼤的,⼤的⽐如有 Polkadot 鏈、Substrate 開發框架、還有中繼/平⾏鍊等等,限於篇幅和注意⼒,我本次從中挑選我最感興趣的執⾏層即虛擬機這條線給⼤家串⼀下它的發展史和當前的狀況以及相關的信息。
前傳
當年以太坊創⽴伊始,出於對以太坊的興趣,Gavin 加⼊了以太坊的項⽬開發,當時以太坊還只是⼀個初步的框架,⽽ Gavin 加⼊之後使得以太坊在技術層⾯上得以落地,我們來看看他在以太坊的貢獻:
(1)完成了以太坊的 PoC-1(Proof of Concept-1);(2)⼏乎是⼀個⼈完成了以太坊最早的 C++ 版本客戶端 ;(3)撰寫了以太坊技術規範⻩⽪書 Yellow Book;(4)發明了⽤於智能合約開發的⾼級語⾔ Solidity。
⾄於這段歷史⼤家有興趣可以閱讀《點對萬物:以太坊與未來數字⾦融》,⽽ Gavin Wood 的傳奇故事⽹上也可以很容易搜索到,此處不再贅述。
讓我們將眼光聚焦到 Solidity 和 evm,⾸先看⼀段簡單的 Solidity 計數器示例代碼:
pragma SOLidity ^0.8.3; contract Counter { uint public count; function get() public view returns (uint) { return count; } function inc() public { count += 1; } function dec() public { count -= 1; } }這個示例聲明了⼀個count的狀態變量,你可以將其視為數據庫中的單個插槽,你可以通過管理數據庫的代碼來查詢和更改它,在此示例中,合約定義了可⽤於修改或檢索變量值的函數 inc,dec和get。
經過 Solildity 編譯器solc編譯後,可以得到⼀段字節碼(⻅下),通過 JSON-RPC 部署到節點後,執⾏層會先共識,確認後交由 EVM 執⾏。
6080604052348015600e575f80fd5b506101d98061001c5f395ff3fe608060405234801561000f575f80fd5 b506004361061004a575f3560e01c806306661abd1461004e578063371303c01461006c5780636d4ce63c14 610076578063b3bcfa8214610094575b5f80fd5b61005661009e565b60405161006391906100f7565b60405 180910390f35b6100746100a3565b005b61007e6100bd565b60405161008b91906100f7565b604051809103 90f35b61009c6100c5565b005b5f5481565b60015f808282546100b4919061013d565b92505081905550565 b5f8054905090565b60015f808282546100d69190610170565b92505081905550565b5f819050919050565b 6100f1816100df565b82525050565b5f60208201905061010a5f8301846100e8565b92915050565b7f4e487 b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f61 0147826100df565b9150610152836100df565b925082820190508082111561016a57610169610110565b5b9 2915050565b5f61017a826100df565b9150610185836100df565b925082820390508181111561019d576101 9c610110565b5b9291505056fea26469706673582212207b7edaa91dc37b9d0c1ea9627c0d65eb34996a5e3 791fb8c6a42ddf0571ca98164736f6c634300081a0033我們換成彙編指令的⽅式來看看它是什麼樣⼦:
PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH1 0xE JUMPI PUSH0 DUP1 REVERT JUMPDEST POP PUSH2 0x1D9 DUP1 PUSH2 0x1C PUSH0 CODECOPY PUSH0 RETURN INVALID PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH2 0xF JUMPI PUSH0 DUP1 REVERT JUMPDEST POP PUSH1 0x4 CALLDATASIZE LT PUSH2 0x4A JUMPI PUSH0 CALLDATALOAD PUSH1 0xE0 SHR DUP1 PUSH4 0x6661ABD EQ PUSH2 0x4E JUMPI DUP1 PUSH4 0x371303C0 EQ PUSH2 0x6C JUMPI DUP1 PUSH4 0x6D4CE63C EQ PUSH2 0x76 JUMPI DUP1 PUSH4 0xB3BCFA82 EQ PUSH2 0x94 JUMPI JUMPDEST PUSH0 DUP1 REVERT JUMPDEST PUSH2 0x56 PUSH2 0x9E JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0x63 SWAP2 SWAP1 PUSH2 0xF7 JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN JUMPDEST PUSH2 0x74 PUSH2 0xA3 JUMP JUMPDEST STOP JUMPDEST PUSH2 0x7E PUSH2 0xBD JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0x8B SWAP2 SWAP1 PUSH2 0xF7 JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN JUMPDEST PUSH2 0x9C PUSH2 0xC5 JUMP JUMPDEST STOP JUMPDEST PUSH0 SLOAD DUP2 JUMP JUMPDEST PUSH1 0x1 PUSH0 DUP1 DUP3 DUP3 SLOAD PUSH2 0xB4 SWAP2 SWAP1 PUSH2 0x13D JUMP JUMPDEST SWAP3 POP POP DUP2 SWAP1 SSTORE POP JUMP JUMPDEST PUSH0 DUP1 SLOAD SWAP1 POP SWAP1 JUMP JUMPDEST PUSH1 0x1 PUSH0 DUP1 DUP3 DUP3 SLOAD PUSH2 0xD6 SWAP2 SWAP1 PUSH2 0x170 JUMP JUMPDEST SWAP3 POP POP DUP2 SWAP1 SSTORE POP JUMP JUMPDEST PUSH0 DUP2 SWAP1 POP SWAP2 SWAP1 POP JUMP JUMPDEST PUSH2 0xF1 DUP2 PUSH2 0xDF JUMP JUMPDEST DUP3 MSTORE POP POP JUMP JUMPDEST PUSH0 PUSH1 0x20 DUP3 ADD SWAP1 POP PUSH2 0x10A PUSH0 DUP4 ADD DUP5 PUSH2 0xE8 JUMP JUMPDEST SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH32 0x4E487B7100000000000000000000000000000000000000000000000000000000 PUSH0 MSTORE PUSH1 0x11 PUSH1 0x4 MSTORE PUSH1 0x24 PUSH0 REVERT JUMPDEST PUSH0 PUSH2 0x147 DUP3 PUSH2 0xDF JUMP JUMPDEST SWAP2 POP PUSH2 0x152 DUP4 PUSH2 0xDF JUMP JUMPDEST SWAP3 POP DUP3 DUP3 ADD SWAP1 POP DUP1 DUP3 GT ISZERO PUSH2 0x16A JUMPI PUSH2 0x169 PUSH2 0x110 JUMP JUMPDEST JUMPDEST SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH0 PUSH2 0x17A DUP3 PUSH2 0xDF JUMP JUMPDEST SWAP2 POP PUSH2 0x185 DUP4 PUSH2 0xDF JUMP JUMPDEST SWAP3 POP DUP3 DUP3 SUB SWAP1 POP DUP2 DUP2 GT ISZERO PUSH2 0x19D JUMPI PUSH2 0x19C PUSH2 0x110 JUMP JUMPDEST JUMPDEST SWAP3 SWAP2 POP POP JUMP INVALID LOG2 PUSH5 0x6970667358 0x22 SLT KECCAK256 PUSH28 0x7EDAA91DC37B9D0C1EA9627C0D65EB34996A5E3791FB8C6A42DDF057 SHR 0xA9 DUP2 PUSH5 0x736F6C6343 STOP ADDMOD BYTE STOP CALLER從編譯原理⻆度看⼀下它的執⾏流程:Solidity 源代碼 -> 詞法解析器 -> 語法解析器 -> 編譯-> 字節碼 -> 虛擬機 -> 解釋執⾏
是的,它在計算機⾏業只是⼀個新的編程語⾔⽽已,可這在當年真的是⼀件很 NB 的事情,在加⼊以太坊開發項⽬的兩年後,以太坊如期上線。⽐特幣是⼀個電⼦⽀付系統,⽽以太坊讓區塊鏈變得,以太坊喊出了“世界計算機”的⼝號,⼀切都變得不⼀樣了。
Web 3.0
Gavin 還在擔任以太坊的聯合創始⼈和 CTO 期間,於 2014 年 4 ⽉ 17 ⽇在博客上發布了⼀篇⽂章:dapps: What Web 3.0 Looks Like[1],全⾯地解釋了他⼼中的 Web3.0 時代應該是什麼樣的,以及構成 Web3.0 的四個組件。 Web3.0 這個概念有多⽕,影響⼒有多⼤,⼤家有⽬共暏,⽽ Gavin 他不僅技術好,還具有前瞻的視野。 關於 Web 3.0 的歷史和爭論,可以看看維基百科 Web3 詞條[2],美國企業家兼⻛險投資家諾瓦·斯⽪瓦克[3]建議將 Web3.0 的定義延伸⾄當前各⼤技術潮流邁向新的成熟階段的具體體現,摘抄如下:
•:寬帶⽹絡的普及和發展,移動通信設備的互聯⽹接⼊。 (例如:平板電腦)
•:“SaaS 服務”的商業模型,Web 服務互⽤性,分佈式計算,⽹格計算和效⽤計算(⼜稱“雲端計算”)。
•:開放 API 和協議,開放資料格式,開源軟件平台和開放資料(如創作共享,開放資料授權)。
•:OPEnID,開放名聲,跨域身份和個⼈資料。
•:語義⽹技術⽐如資源描述框架,⽹絡本體語⾔,SWRL,SPARQL 語義應⽤程序平台和基於聲明的資料存儲。
•:萬維數據庫(“World Wide DatABase”,由語義⽹的技術實現)。
•:普通語⾔的處理,機器學習,機器推理,⾃主代理。

2015年年底,Gavin 從以太坊離開。
隨後他創⽴了,並開發了⼀款⽤ Rust語⾔編寫的以太坊客戶端,⼀度壟斷以太系錢包市場。 Gavin 離開以太坊的原因,我們不得⽽知,我們能知道的是。
“以太坊對我來說是⼀個實驗,⼀個驗證技術是否可⾏的產品原型。以太坊也是我的學校,我從這個學校畢業了,我想嘗試做更多的事情。”
"其實我從以太坊學到最多的並不是技術(當時以太坊有⼀個專⻔負責管理技術細節的研究團隊),⽽是社會經驗。治理就是其中之⼀。我認為在區塊鏈系統中,通過治理提升系統的能⼒是很重要的,這會是⼀個⾰命性的新特性,⽽這恰恰是以太坊沒有做的事情。“
在以太坊期間,Gavin ⼀直是實踐者,但是他不是設計者,⽽他也⼀直在醞釀新的創新。
波卡的誕⽣
⼀年後,Gavin 解開了⼀直以來縈繞在⼼頭的難題,並在16年發布了波卡⽩⽪書。 很多⼈都知道,波卡除了解決擴容問題之外,還想讓各⾃獨⽴的區塊鏈能進⾏通信,也就是跨鏈問題。 但是提到波卡,你最應該知道的是:分⽚,。
POLkadot 創始⼈ Gavin 的原話更能說明這⼀點:
"PolkaDOT (波卡) 的設計邏輯並沒有直接聯想到互操作性。我們在等以太坊的分⽚技術推出。但分⽚⼀直沒有實現,現在也沒有推出。因此我想⾃⼰做⼀個擴展性更強的“以太坊”,在設計過程中將分⽚概念推到了⼀個⽐較極端的程度,就⼲脆不要分⽚了,設計獨⽴的鏈就⾏。這樣設計的話,不同鏈之間就可以互相傳遞信息,最終的結果是通過⼀個共享的共識層⾯來實現通信。 "
那我們應該如何理解分⽚呢? ⾸先我們聊⼀下以太坊的困境,⼀直以來,以太坊的性能問題是其硬傷,2018年就因為⽕熱的加密貓爆發了嚴重的擁堵,不僅增加了轉賬時間,還使得轉賬⼿續費居⾼不下。 就如同銀⾏辦理業務,銀⾏只有⼀個窗⼝並且處理速度較慢,當處理業務的⼈變多的時候,就會排起⻓隊,等待辦理。
但是,如果銀⾏有好⼏個窗⼝,同時辦理業務,可能就不需要排隊了。 這就是分⽚的基礎邏輯,,極⼤的提升了效率。
所以在波卡中,每個分⽚都承載了核⼼邏輯,並且允許它們並⾏交易、交換數據,最終讓多個區塊鍊鍊接到⼀個⽹絡上。
波卡的出現,不僅僅是媲美甚⾄超過以太坊2.0的設想,更具有創造性的地⽅是:。 它不再是⼀個單純的區塊鏈,“波卡想為各種社會創新提供⼀個真正開放⾃由的平台”。
這是 Web3 的理想,是 Polkadot 的理想,也是 Gavin 的理想。
Polkadot 1.0
Polkadot 的中繼鏈本身並不⽀持智能合約,但其連接的平⾏鏈[4]可以⾃由定義狀態轉換規則,因此能夠提供智能合約功能。 與其他⽣態系統的區別在於,在 Polkadot 的背景下,平⾏鍊和智能合約存在於堆棧的不同層:智能合約位於平⾏鏈之上。 平⾏鏈通常被描述為第 1 層區塊鏈 — 不同之處在於它們不必構建⾃⼰的安全性,並且可以升級和互操作。
Polkadot 為開發⼈員構建智能合約提供了靈活性,既⽀持由 EVM(以太坊虛擬機)執⾏的 Solidity 合約,也⽀持使⽤ ink! 的基於 Wasm 的合約:
與 EVM 兼容的合約
Polkadot ⽣態⽀持以太坊虛擬機(EVM),這得益於 Frontier[5]這⼀⼯具集。 Frontier 允許基於 Substrate 的區塊鏈原⽣運⾏以太坊智能合約,並通過兼容以太坊的 API/RPC 接⼝,實現與以太坊⽣態的⽆縫交互。 合約使⽤Solidity 或 Vyper 等語⾔編寫,EVM 在區塊鏈中被⼴泛標準化,包括 Astar、MoonBEAM 和 Acala 等 Polkadot 平⾏鏈。 這種兼容性允許合約以最少的修改部署在多個⽹絡中,從⽽受益於完善、⼴泛的開發⽣態系統。
舉個例⼦:Astar[6]是 Polkadot 上的關鍵智能合約平台,其獨特的多虛擬機⽅法同時⽀持 EVM 和 WebAssembly(Wasm) 智能合約。 這種雙 VM ⽀持允許開發⼈員選擇他們喜歡的編程環境,同時保持與以太坊的完全兼容性。 該平台的運⾏時[7]使⽤ FRAME 在 Substrate 上構建,結合了來⾃ Polkadot-SDK 的關鍵組件以及⽤於處理其獨特功能的定制模塊。

這些鏈通常採⽤現成的合約模塊,並在其基礎上進⾏⼀些額外的創新。 例如:Phala[8]:在可信執⾏環境中使⽤合約模塊,實現機密的智能合約執⾏和互操作性。 Aleph Zero[9]:在零知識環境中使⽤合約托盤。 t3rn[10]:使⽤合約模塊作為構建模塊,實現智能合約的多鏈執⾏。 查看更多如 Aster、MoonBeam、Acala 等相關兼容 EVM 的平⾏鏈的技術參⻅官⽅⽂檔:平⾏鏈合約[11]
Wasm (ink!) 合約:
Polkadot 通過 FRAME 框架提供 Contracts 模塊[12],該模塊採⽤ WebAssembly 作為合約的運⾏環境。 理論上,任何可以編譯為 Wasm 的語⾔都能⽤於開發智能合約,但出於優化和便捷性考慮 Parity 專⻔推出了 ink![13]。
在討論 ink! 之前,我們⾸先需要澄清什麼是 Substrate[14]及其合約模塊 (pallet-contracts)。 Substrate 是⼀個⽤於構建區塊鏈的框架,可以是獨⽴的區塊鏈,也可以是連接到 Kusama[15]或 Polkadot[16]的區塊鏈,即所謂的平⾏鏈。
Substrate 包含許多模塊,在 Substrate 術語中稱為模塊。 Substrate 附帶⼀組模塊,可滿⾜現代區塊鏈通常具有的許多要求 — — 質押、同質化代幣、⾮同質化代幣、治理等。
Substrate 還附帶⼀個⽤於智能合約的模塊,稱為 Contracts pallet。 如果在 Substrate 中開發了平⾏鏈,則可以通過包含此 pallet 輕鬆添加智能合約功能。 該模塊只是執⾏環境,它以 WebAssembly ⽂件作為輸⼊。 該模塊的智能合約必須編譯為 WebAssembly (Wasm) ⽬標架構。
ink! 在這⾥是如何發揮作⽤的? ink! 是⼀種編程語⾔,具體來說,它是流⾏的 Rust 編程語⾔的嵌⼊式領域特定語⾔ (eDSL)。 這意味著您可以使⽤所有常規 Rust 語法以及新添加的⼀些細節,使該語⾔適合智能合約世界。 合約模塊接收這些 ink! 合約並以安全的⽅式執⾏它們。 簡⽽⾔之:
使⽤ ink! 您可以⽤ Rust 為使⽤ Substrate 構建的包含 Contracts 托盤的區塊鏈編寫智能合約。
ink! 並⾮創建⼀種新語⾔,它只是具有明確“合約格式”的標準 Rust,並帶有專⻔的#[ink(…)]屬性宏。 這些屬性宏告訴 ink! Rust 智能合約的不同部分代表什麼,並最終允許 ink! 執⾏創建與 Polkadot SDK 兼容的 Wasm 字節碼所需的所有魔法。 由於 ink! 智能合約被編譯為 Wasm,因此它們通過沙盒執⾏提供了⾼執⾏速度、平台獨⽴性和增強的安全性。
讓我們看⼀個簡單的 ink! 合約。 此處的合約在其存儲中保存⼀個布爾值。 合約創建後,它會將布爾值設置為true。 合約公開兩個函數:⼀個⽤於讀取布爾值的當前值(fn get()),另⼀個⽤於將值切換為其相反的布爾值(fn FLIP())。
#[ink::contract] mod flIPper { #[ink(storage)] pub struct Flipper { value: bool, } impl Flipper { #[ink(constructor)] pub fn new(init_value: bool) -> Self { Self { value: init_value } } #[ink(message)] pub fn flip(&mut self) { self.value = !self.value; } #[ink(message)] pub fn get(&self) -> bool { self.value } }}看著與 Solidity 所提供的能⼒差不多,Storage 存儲、Message 消息、Errors 錯誤,Event 事件等。 對於合約開發者來說,這意味著他們可以使⽤ ink! 編寫智能合約,但也可以決定使⽤其他語⾔: Solidity 的 Solang 編譯器[17]和 AssemblyScript 的 ask
import { env, Pack } frOM "ask-lang";import { FlipEvent, Flipper } from "./storage";@contractexport class Contract { _data: Pack>(new Flipper(false)); } get data(): Flipper { return this._data.unwrap(); } set data(data: Flipper) { this._data = new Pack(data); } @constructor() default(flag: bool): void { this.data.flag = flag; } @message({ mutates: true }) flip(): void { this.data.flag = !this.data.flag; let event = new FlipEvent(this.data.flag); // @ts-ignore env().emitEvent(event); } @message() get(): bool { return this.data.flag; }}
添加新語⾔並不難。 只需要有⼀個適⽤於 WebAssembly 的語⾔編譯器,然後就可以實現 Contracts pallet API。
⽬前,此 API 包含⼤約 15-20 個函數,可⽤於智能合約可能需要的任何功能:存儲訪問、加密功能、環境信息(如區塊編號)、訪問⽤於獲取隨機數或⾃⾏終⽌合約的函數等。 並⾮所有這些都必須⽤該語⾔實現— ink! “Hello,World!” 只需要六個 API 函數。 以下模式描述了這種關係:

與 EVM 相⽐,選擇 ink! 和 Contracts pallet 有許多優勢。 總結⼀下本⽂中詳細介紹的⼀些優勢:
• ink! 只是 Rust — 您可以使⽤所有常規的 Rust ⼯具:clippy, crates.io[19], IDE 等。
• Rust 是⼀種融合了多年語⾔研究的語⾔,它安全且快速。 除此之外,從較舊的智能合約語⾔(如 Solidity)中吸取了主要經驗,並將其融⼊到 ink! 語⾔設計中。 選擇了更合理的默認⾏為,例如默認禁⽤ ⼊或默認將函數設為私有。
• Rust 是⼀種令⼈驚嘆的語⾔,它在 StackOverflow 上連續七年被評為最受歡迎的編程語⾔(來源[20])。
• 如果您是⼀家公司並希望聘請智能合約開發⼈員,您可以從 Rust ⽣態系統中聘請,該⽣態系統⽐ Solidity 開發⼈員的利基市場要⼤得多。
• ink! 是 Substrate 原⽣的,使⽤類似的原語,就像相同的類型系統⼀樣。
• 從合約到平⾏鏈的遷移路徑⾮常清晰。 由於 ink! 和 Substrate 都是 Rust,開發⼈員可以 ⽤⼤部分代碼、測試以及前端和客戶端代碼。
• WebAssembly 是⼀個⾏業標準,它不僅僅⽤於區塊鏈世界。 它由 Google、Apple、Microsoft、Mozilla 和 Facebook 等⼤公司不斷改進。
• WebAssembly 擴展了智能合約開發⼈員可⽤的語⾔系列,包括 Rust、C/C++、C#、Typescript、Haxe、Kotlin 等。 這意味著您可以使⽤任何您熟悉的語⾔編寫智能合約。 這種設計⽐語⾔和執⾏架構的緊密耦合更具前瞻性。
關於本節內容,可以閱讀 What is Parity’s ink!?[21]了解更多信息。
Polkadot 2.0 / JAM
2⽉28⽇ Gavin Wood 在 JAM Tour 上海站⾸次公開演示了在 JAM 上運⾏ DOOM 遊戲! 這是區塊鏈⾏業的⼀次歷史性時刻,JAM 使得區塊鏈不僅僅是⼀個⽹絡,⽽是⼀個強⼤的超級計算機平台,⽀持像 DOOM 這樣的常規軟件運⾏,同時提供強⼤的計算能⼒和低延遲。

拋開其它先不談,我們還是只看虛擬機,基於我這邊⼏天對 PolkaVM 相關源代碼的閱讀,它確實是真實發⽣的,關鍵點在 PolkaVM 有 Host 運⾏了 VM,向上提供了導出接⼝,然後在 VM 上運⾏了 guest 版 Doom 的 RISC-V 版代碼。
源代碼在此:
https://github.com/paritytech/polkavm/tree/master/examples/doom
讓我們看⼀下,這次最關鍵的新智能合約解決⽅案的⼏個組件:
Revive Pallet
Revive Pallet[22]為運⾏時提供部署和執⾏ PolkaVM 智能合約的功能。 它是⼀個經過⼤量修改的pallet_contracts分⽀。 這些合約可以⽤任何編譯為 RISC-V 的語⾔編寫。 ⽬前,唯⼀官⽅⽀持的語⾔是 Solidity(通過 revive[23])和 Rust(查看fixtures⽬錄中的 Rust 示例)。
在合約語⾔層⾯兼容以太坊:可以⽤ Solidity 編寫合約,並使⽤以太坊 JSON RPC 和 MetaMask等以太坊錢包與節點交互。 在底層,合約從 YUL 重新編譯為 RISC-V,以使⽤ PolkaVM ⽽不是 EVM 運⾏它們。 為簡化操作,可使⽤定製版的 REMIX[24]Web 前端將合約編譯為 RISC-V 並將其部署到 Westend Asset Hub Parachain[25]。
Revive
Revive[26]是 “Solidity to PolkaVM”編譯器項⽬的總稱,它包含多個組件(例如 YUL 前端以及resolc可執⾏⽂件本身)。 resolc是單⼊⼝點前端⼆進制可執⾏⽂件的名稱,它透明地使⽤所有 revive 組件來⽣成編譯的合約⼯件。 可以將 Solidity 合約編譯為 PolkaVM 可執⾏的 RISC-V 代碼,從⽽實現 Solidity 合約在 Polkadot 上的運⾏。
為此需要⼀個編譯器。 它的⼯作原理是使⽤原始solc編譯器,然後將其中間表示 (YUL) 輸出重新編譯為 RISC-V。 LLVM[27]是⼀種流⾏且功能強⼤的編譯器框架,⽤作編譯器後端,並在優化和 RISC-V 代碼⽣成⽅⾯承擔了繁重的⼯作。 revive主要負責將⽣成的 YUL 中間表示 (IR) 降低solc到 LLVM IR。

這種⽅法在保持⾼⽔平的以太坊兼容性、良好的合約性能和可⾏的⼯程努⼒之間提供了良好的平衡。 與實現完整的 Solidity 編譯器相⽐,這樣做的好處是任務要⼩得多。 通過選擇這種⽅法,就能⽀持 Solidity 及其所有不同版本的所有怪癖和怪異之處。
PolkaVM
PolkaVM[28]是⼀個基於 RISC-V 的通⽤⽤戶級虛擬機。 這是相對於競爭技術做出的最明顯的改變。 使⽤新的⾃定義虛擬機⽽不是 EVM 來執⾏合約。 ⽬前,在運⾏時本身中包含了⼀個 PolkaVM 解釋器。 稍後的更新將提供在客戶端內運⾏的完整 PolkaVM JIT。

• 默認情況下是安全的且處於沙盒中。 虛擬機中運⾏的代碼應在單獨的進程中運⾏,並且不應能夠訪問主機系統,即使在虛擬機內部存在具有完全遠程代碼執⾏權限的攻擊者的情況下也是如此。
• 執⾏速度快。 VM 中運⾏的代碼的運⾏時性能應該與最先進的 WebAssembly VM 相媲美,⾄少在同⼀數量級內。
• 編譯速度快,保證單次編譯 O(n)。 將新代碼加載到虛擬機中應該⼏乎是即時的。
• 內存佔⽤低。 虛擬機的每個並發實例的基準內存開銷不應超過 128KB。
• ⼩型⼆進制⽂件。 為此 VM 編譯的程序應佔⽤盡可能少的空間。
• 不浪費虛擬地址空間。 VM 不應為沙盒⽬的預先分配 GB 級的虛擬地址空間。
• 完全確定性。 給定相同的輸⼊和相同的代碼,執⾏應該始終返回完全相同的輸出。
• ⽀持⾼性能異步 gas 計量。 Gas 計ᰁ應便宜、確定且合理準確。
• 很簡單。 ⼀個程序員可以在不到⼀週的時間內編寫出⼀個與該虛擬機完全兼容的解釋器。
• 版本化操作語義。 未來對客戶程序可觀察到的語義的任何更改都將被版本化,並明確選擇加⼊。
• 標準化。 應該有⼀個規範來完整描述此虛擬機的客戶可觀察的操作語義。
• 跨平台。 在未受⽀持的操作系統和平台上,VM 將以解釋模式運⾏。
• 最⼩外部依賴。 虛擬機應該基本獨⽴、編譯速度快,並且能夠抵禦供應鏈攻擊。
• ⽤於調試和性能分析的內置⼯具。
與 EVM 的兩個根本區別是:
• 寄存器機- EVM 是堆棧機。 這意味著函數的參數在⽆限堆棧上傳遞。 PolkaVM 基於 RISC-V,它是⼀個寄存器機。 這意味著它在⼀組有限的寄存器中傳遞參數。 這樣做的主要好處是,由於這些都是寄存器機,因此它使底層硬件的轉換步驟更加⾼效。 仔細選擇了寄存器的數量,使它們⼩於臭名昭著的寄存器匱乏的 x86-64 指令集。 允許將 NP-hard 寄存器分配問題簡化為簡單的 1 對 1 映射是 PolkaVM 快速編譯時間的秘訣。
• 減⼩字⻓- EVM 使⽤ 256 位的字⻓。 這意味著每個算術運算都必須對這些⼤數字執⾏。 這使得任何有意義的數字運算都⾮常慢,因為它必須轉換為許多本機指令。 PolkaVM 使⽤ 64 位的字⻓,底層硬件原⽣⽀持該功能。 也就是說,當通過 YUL(#Revive)轉換 Solidity 合約時,仍然會使⽤ 256 位算術,因為 YUL 太低級,⽆法⾃動轉換整數類型。 但是,完全可以⽤不同的語⾔編寫合約並從 Solidity ⽆縫調⽤它。 想像⼀個系統,其中業務邏輯⽤ Solidity 編寫,但底層架構⽤更快的語⾔編寫,類似於 Python,其中⼤部分繁重的⼯作由 C 模塊完成。
總結
智能合約編程語⾔
智能合約語⾔的數量逐年增加。 選擇第⼀種語⾔可能具有挑戰性,尤其是對於初學者⽽⾔。 選擇主要取決於您感興趣的⽣態系統,儘管有些語⾔適⽤於不⽌⼀個平台。 每種語⾔都有⾃⼰的優點和缺點,本⽂不打算⼀⼀介紹。
有⼈會問為什麼會有這麼多合約語⾔? 我總結有這麼⼏種可能:1)已有語⾔不能滿⾜鏈的專有特性;2)更好的性能、安全、成本;3)品味 :)
我本⼈對合約語⾔沒有偏⻅性,以下摘抄⼏個不同的聲⾳,從中也能獲取⼀些不同的收穫,⽆論是從使⽤者⻆度,還是語⾔或 VM 設計的⻆度。
• 為什麼不使⽤ Solidity? Solidity 是備受推崇的先驅,但它受制於 EVM 的許多歷史怪癖。 它缺乏程序員所期望的常⻅功能,類型系統相對缺乏表現⼒,並且缺乏統⼀的⼯具⽣態系統。 在 Sway 中,我們讓您使⽤⼀整套現代⼯具來設計智能合約。 您將獲得⼀種功能⻬全的語⾔,其中包括泛型、代數類型和基於特徵的多態性。 您還可以獲得⼀個集成、統⼀且易於使⽤的⼯具鏈,其中包含代碼完成 LSP 服務器、格式化程序、⽂檔⽣成以及運⾏和部署合約所需的⼀切,這樣您就可以輕鬆實現所需的功能。 我們的表達類型系統允許您捕獲語義錯誤,我們提供良好的默認值,並進⾏⼴泛的靜態分析檢查(例如強制執⾏檢查、效果、相互作⽤您可以使⽤ java.lang.Integer.pattern 來確保在編譯時編寫出安全、正確的代碼。
via https://docs.fuel.network/docs/sway/
• 為什麼不使⽤ Rust? 雖然 Rust 是⼀種出⾊的系統編程語⾔(Sway 本身也是⽤ Rust 編寫的),但它並不適合智能合約開發。 Rust 之所以出⾊,是因為它可以使⽤零成本抽象及其複雜的借⽤檢查器內存模型,為沒有垃圾收集器的複雜程序實現令⼈印象深刻的運⾏時性能。 在區塊鏈上,執⾏和部署的成本是稀缺資源。 內存使⽤率低,執⾏時間短。 這使得複雜的內存管理通常過於昂貴⽽不值得,⽽ Rust 的借⽤檢查器則成為⼀種負擔,沒有任何好處。 通⽤編程語⾔通常不適合這種環境,因為它們的設計必須假設在通⽤計算環境中執⾏。 Sway 試圖通過提供熟悉的語法和適合區塊鏈環境特定需求的功能,為智能合約開發⼈員帶來 Rust 的所有其他優勢,包括其現代類型系統、安全⽅法和良好的默認值。
via https://docs.fuel.network/docs/sway/
• Clarity 代碼的解釋和提交完全按照編寫⽅式進⾏。 Solidity 和其他語⾔在提交到鏈之前會被編譯為字節碼。 編譯智能合約語⾔的危險有兩個⽅⾯:⾸先,編譯器增加了⼀層複雜性。 編譯器中的錯誤可能會導致字節碼與預期不同,從⽽帶來引⼊漏洞的⻛險。 其次,字節碼不是⼈類可讀的,這使得很難驗證智能合約實際上在做什麼。 問問⾃⼰,你會簽署⼀份你⽆法閱讀的合約嗎? 如果你的答案是否定的,那麼智能合約⼜有什麼不同呢? 有了 Clarity,你所看到的就是你所得到的。
via https://docs.stacks.co/concepts/clarity\#clarity-is-interpreted-not-compiled
虛擬機及指令集
從我們熟知的 JVM 到 EVM 在區塊鏈領域有啟發式崛起,就我所知的虛擬機就有⼏⼗個,不同的虛擬機實現⽅式不同,其中最核⼼的解釋器(Interpreter)通常是最基礎的執⾏⽅式之⼀,特別是在 Just-In-Time (JIT) 編譯或Ahead-Of-Time (AOT) 編譯之外,Interpreter 仍然⼴泛⽤於代碼執⾏,它負責解析和執⾏字節碼。
解釋器示例
下⾯⽤⼀個⼩示例演示⼀個最基礎本質的解釋器,暫不考慮各種優化機制。
use std::collections::VecDeque; #[derive(Debug, Clone, Copy)] enum OpCode {Push(i32), Add, Sub, Mul, Div, Print} struct Interpreter { stack: Vec指令集分類
從上⾯的解釋器示例來看,指令的數量和可擴展性決定了⼀個虛擬機的⼤⽅向,按我的分析,虛擬機可以按指令集歸類,⼤致可以分成下⾯ 4 類:
1. EVM 代表的⾃制指令集,導致要做⼤量的重複⼯作,⽐如編譯器、指令設計... 在 EIP-3855[29]之前已經有 141條指令了,⽽且還在不斷增加新的指令。
2. WASM,開箱即⽤,⽀持多種語⾔,但⽐較重,在 Polkadot ⽂檔中也提出 WASM 這⼀點。 我是覺得,WASM 的優勢在於天然的多語⾔⽀持,如果某個鏈只是想運⾏⼀段邏輯,⽐較野蠻地將狀態寫⼊ account storage slot,那就⽐較合適。 快速開發,快速接⼊,但缺點是其指令並不是專為區塊鏈設計的,想⽀持⼀些特殊能⼒,想裁剪就很難了 (150+ 條指令[30])。
這⾥另外也要提到 Solana 的 SVM,它執⾏的是 BPF 指令,可以將任意編程語⾔編譯成 BPF 指令的動態鏈接庫,只是相對 WASM 要相對⼩眾⼀些。
3. 基於 RISC-V 指令集,⽐如 PolkaVM 和當前很多的 zkVM 系:ceno、eigen zkvm、jolt、mozak vm、nexus、o1vm、openvm、powdrVM、risc0、sp1、sphinx 等等。 其它我還沒來得及研究,只說我知道的PolkaVM 和 Nexus zkVM,它實現了通⽤的 RISC-V 指令解釋,⽬前多數語⾔經過 GCC 或 LLVM 編譯後都是可以輸出為 RISC-V 指令的, 這就從語⾔層⾯上打開了視角,為未來多語⾔的⽀持打好了基礎。 基礎 RV32I 指令是 47 條,可選擴展,對⽐ x86 3000+條指令,真的算是精簡了。
4. 解釋型,⽐如 Stacks 的 Clarity(Lisp),AO 的 Lua,Mina 的 Typescript 等。 所以,總結來說,如果左⼿是 EVM 代表私有指令,右⼿邊是開放性的 WASM 的話,RISC-V 就是⼀個⽐較平衡的⼆次開發的好選擇,⾸先,各編譯器已經⽀持此指令集,可參考實現⽐較多。 其次,很容易基於它做⾃定義指令的設計。
額外:探索多鏈智能合約的未來
隨著區塊鏈⽣態的多樣化發展,越來越多的項⽬為⽀持獨特功能⽽推出了⾃⼰的智能合約語⾔(如 Solidity、Move、Cairo、Sway 等)。 這種創新雖然豐富了技術棧,但也帶來了新的學習成本和兼容性挑戰。 ⼀個核⼼問題逐漸浮現:能否將成熟的 Solidity 合約⽆縫部署到新興的區塊鏈上?
為此,我們聯合⼏位對編譯器技術充滿熱情的研究者,共同發起了 Hummanta 編譯器項⽬。 該項⽬旨在打破鏈間壁壘,實現智能合約語⾔的跨鏈編譯,⽀持將 Solidity、Move、Cairo、Sway 等語⾔編譯到多種虛擬機環境,包括:EVM 和新興 VM:PolkaVM、SolanaVM、MoveVM、FuelVM 等。

項⽬當前進展
我們正從三個⽅向推進這⼀願景:
1. 語法與指令集標準化系統整理各語⾔的語法定義和虛擬機指令集,為跨鏈編譯奠定基礎。
2. 編譯器⼯具鏈開發基於 LALRPOP[31]構建解析器,利⽤ Cranelift[32]/ LLVM 實現代碼⽣成,⽀持多語⾔到多⽬標字節碼的轉換。
3. ⼀體化開發體驗開發 Hummanta CLI ⼯具[33](類似 Cargo),提供從編譯到部署的全流程⽀持。