Jolestar | 智慧合約程式設計語?,還可能有哪些創新點?

買賣虛擬貨幣

9月4日的 bewater devcon 2021 全球開發者大會上,westar 首席架構師,starcoin 核心開發者 jolestar(王淵命) ,就智慧合約程式語言,還可能有哪些創新點和大家展開了討論。


move 智慧合約程式語言,存在3個創新點:


1、有一套狀態操作協議,把狀態所有權明確了

2、有一套型別機制,實現了型別系統在不同的合約之間共享

3、改變了合約直接的依賴關係,改為了鏈上的依賴方式


bewater


大家好,我是王淵命,今天跟大家分享一些工程師比較關心的話題。

在介紹話題之前我先自我介紹一下,我是一個老程式設計師了,各種語言也用過,所以我今天分享的角度是從一個工程師的角度。我們工程師視角和語言學家視角不一樣的,我們是俠客,要找一把趁手的武器去行走江湖。


我最早是在網際網路行業,2017 年進入區塊鏈,當時我們組織了一個叫 bftf 的技術社羣定期組織一些 meetup 來學習區塊鏈相關的技術,後來我們開始成立 westar 實驗室,做一個新的公鏈專案 starcoin,bftf 社羣就沒有顧上了。一方面是忙,另外一方面是自己已經是專案方的角色,很難組織中立的社羣,所以我很高興看到 bewater 這樣中立的技術社羣出現,讓我們大家純粹的在一個平臺上互相探討。


starcoin 在今年 5 月 18 號上線,是 pow 的鏈。迴應下上午嘉賓提到的幾個點——一個是 pow,一個是鏈上治理。


關於 pow:


區塊鏈未來怎麼發展各鏈有不同的假設,我們的假設是:一層不解決吞吐的問題,二層解決吞吐的問題,所以一層使用了中本聰共識的改進版,更關鍵是我們要思考「狀態怎麼去處理」這個難題,這個與一二層機制的設計是非常相關的。


關於鏈上治理:


我們引入鏈上治理機制來實現鏈和系統合約的治理升級,並且 dao 本身的合約也可以升級,dao 是可以自舉的。從主網上線以來已經經歷了五次治理升級。


還有一點是我們引入了 move 語言,我們用 move 語言最關鍵的是透過它,解決了鏈上狀態拆分的關鍵痛點。


關於我自己的主張,我自己總結叫做 blockchain maximalism,這個是從 bitcoin maximalism 演化而來。bitcoin maximalism, 比特幣最大主義者,或者通常翻譯叫「位元神教」。對位元神教的人問一個關於未來的問題,他會給一個比特幣的答案,而 blockchain maximalism 往往會給一個區塊鏈的答案。


早上其它嘉賓也說了,我也認同未來會是個是個多鏈的世界。我們已經看到區塊鏈未來的藍圖,但是現在我們還沒找到具體的路,每個方向都有人在探索,我們選了一個方向,但是別人選另外一個方向。我並不會說你選的不對,因為有人嘗試了一個你沒有功夫去嘗試的方向,你應該是鼓勵他的,他幫你去探索另外一條方向,總體上增加了未來藍圖實現的可能。


bewater

一、程式語言的演化


1、傳統程式語言的演化

在探討智慧合約程式語言有沒有新的創新點之前,我們先要回歸到傳統的程式語言。


我們可以看一下,我們常用的這些程式語言的進化發展,總是在這兩個點上在權衡,要麼去照顧一下開發效率,要麼去照顧一下執行效率。


c++ 執行效率好,開發效率差;後來有了其他語言來做執行時垃圾回收,動態執行等提高開發效率的事情;最後 rust 又出現了,更偏向於執行效率,但它實現了編譯期的垃圾回收,不需要手動釋放記憶體,也兼顧了開發效率。


程式設計師的視角來看,所有程式語言都是圖靈等價的。一個電商網站用什麼語言寫,終端使用者會有感覺嗎?對業務能力表達也沒多大影響。所以,我們經常會有一種觀念,就是說在圖靈機器的角度,程式語言不重要,這是我們經常說的觀點。


2、智慧合約帶來的改變

但是我們如果迴歸到智慧合約,我們發現不一樣了:


1)我們應用程式的狀態被鏈託管了。應用程式不用關心程式的狀態,語言裡和處理狀態的特性,無論是檔案系統還是網路,在智慧合約程式語言裡都被幹掉了。智慧合約只關心業務邏輯,不關心狀態怎麼儲存。


2)語言本身的特性也會有很多的變化。最簡單的,在智慧合約程式語言裡,對終端使用者有感知了。智慧合約裡面的賬號地址是一個型別。以前的語言裡,我們在程式碼裡定義了某一種型別來標誌一個使用者,但是使用者的狀態和許可權,並沒有在程式語言層去約束。


3)還有一點是服務之間的依賴關係不一樣了。在以前,依賴別的程式,要麼依賴它的一個演算法庫,要麼透過遠端呼叫依賴它的一個服務,但現在這兩個合二為一了。這個到後面我們說合約間的呼叫的時候可能更明白一些。


智慧合約程式語言的幾個關鍵問題

現在我有兩個大家經常討論的反共識的點


1)有沒有必要發明新的語言?


這也是所有從網際網路圈子裡過來的朋友問的第一個問題,為什麼你們不能用已有的我熟悉的語言去寫智慧合約,用java寫不好嗎,不香嗎,或者php更簡單一些。


這個我們前面已經說了,實際上如果把傳統程式語言裡的很多特性裁掉,智慧合約中併發不存在了,狀態處理不存在了,執行緒,檔案系統,網路等和作業系統相關的庫都幹掉之後,你發現傳統的程式語言庫裡也沒有多少能用的。所以如果你用傳統程式語言也是裁減過的語言,傳統程式語言在這裡沒多少優勢。


2)solidity 和 evm 是不是智慧合約的終點?


早上也有老師分享了這個觀點,認為 solidity 已經是個事實的標準了,跟當初的 javascript 一樣,新的鏈要麼相容 evm,要麼就是在競爭中處於劣勢,這是大家認為的一種觀點。但是這點我是不同意的。我認為 solidity 和 evm 是智慧合約的起點,智慧合約語言的演化才剛剛開始。


從上面的分析可以看出,智慧合約其實讓程式語言的特性發揮了更大的作用。我們透過下面這個例子再說明一下。

簡單的看一下,在傳統的系統中,不同的機構之間透過 rpc 呼叫串在一起。它有各種牆,網路的牆,還有人工的牆(比如必須人工介入的流程)。我們的程式在後面,被各種牆保護著。

但是區塊鏈系統裡不一樣了,所有智慧合約在一個程序內,相當於銀行、交易市場這些機構的程式在同一個程序裡互相之間呼叫,相當於把遠端呼叫變成了系統內部的一種呼叫方式,牆被拆的只剩很薄的一層,安全機制就依賴於程式語言的安全。


傳統程式語言裡,比如說有一個方法,設定成 public 或者 private,對程式影響有多大?它主要影響的是程式的優雅性,你把不該暴露的東西暴露給別人了,不優雅,可能導致升級時候的 break。


但對你的系統安全性有影響嗎?影響不大,因為你的安全不是靠程式的邊界。但在智慧合約裡不一樣了,private 設成 public 那可能你的錢就沒了,這個就是智慧合約讓程式語言的特性發揮出最大的作用。


bewater

二、智慧合約程式設計語?的創新點


從這個角度我們來看智慧合約還有哪些創新點?

1、狀態儲存


現在鏈已經託管了狀態,但是鏈怎麼樣託管狀態,這個事情我們是可以去創新的。


我們簡單的以evm solidity為例,下面這個是token的例子。

程式碼文件,可在“閱讀原文”中複製


這個合約裡宣告一些屬性,直接進行讀寫。整個合約裡看不到處理狀態相關的程式碼,比如讀寫檔案,運算元據庫啥的。第一次從網際網路應用切過來的程式設計師會覺得很驚訝。這個在我們以前寫程式做夢都想這樣的,我不需要調資料庫去寫去讀,鏈把這個屬性自動對映到儲存裡去託管掉了,對程式完全透明。


但這種處理方式帶來的問題是什麼呢?


一個是它這樣對映到儲存裡之後,它的儲存裡存的是什麼東西是很難推斷的。如果直接讀以太坊的外部儲存,想知道里面存的什麼東西其實是非常難的,你必須透過合約讀一遍。


另外一個問題是它的狀態全部在合約裡了,每個使用者的資料都存在合約內部,比如上面中的餘額,並沒有把使用者的狀態拆分出來。這樣很難進行狀態計費,也很難解決狀態爆炸的問題。


我們再看看 move 的處理方式。


程式碼文件,可在“閱讀原文”中複製


move 並沒有完全透明地對映狀態和儲存,而是提供了一些新的操作原語:move_to、move_from。開發者要透過智慧合約明確告訴鏈,某個物件要存在哪個賬號下。


每個使用者的狀態相當於一個透過型別做 key 的 map。外部儲存可以知道這個 key 下存的二進位制資料是什麼型別,可以直接透過型別解析出來,用起來非常容易。最關鍵的是這個狀態是分散到使用者空間的,不需要集中地存在整個合約的狀態下。


2、型別系統

另外一個創新點,就是型別系統。型別系統到底能用來幹什麼?程式語言的型別系統其實理論已經非常早了,我們有好多種型別系統,線性(linear)型別、仿射(affine)型別,這兩種是大家常談的。


如 rust 使用了仿射型別,它要求每個變數最多使用一次。這個規則有什麼用呢?這個規則用來對映 rust 裡的 move 語義—— 這個變數我如果給你了我就不能再用了。這樣解決垃圾回收的問題——我可以在編譯期追蹤這個物件的生命週期,自動的把垃圾回收掉,不需要動態的用一個垃圾回收機制做這個事情。但是這樣的東西在傳統程式語言裡頂多就發揮這樣一個價值,就是幫你做垃圾回收。


其實有非常多型別系統的玩法,但這些玩法在傳統程式語言裡找不到價值。在智慧合約語言裡能不能找到價值呢?

move 引入了一套 ability 的機制,它在型別系統里加了幾種約束,型別可不可以被 copy,可不可以被drop,可不可以被 store,可不可以做一個全域性儲存的 key,四種 ability。


不可以被 drop 的型別,在傳統程式語言裡找不到場景。你 new 出來個物件,但不允許直接丟棄,要表達什麼東西呢?程式重啟的時候記憶體的東西不就都丟了?


在智慧合約語言裡,我們可以用它來表達資產。給一個型別,代表 token,或者 nft,它不能被 drop,必須存下,或者顯示的銷燬。

這是一個 nft 的例子,這在 move 裡是一個 struct 結構。


然後它有兩個泛型引數來提供擴充套件能力。一種是表達它的後設資料,後設資料是可以 copy、store、drop 的,但還有一種是 body,只能 store,不能被 drop。


這用來存什麼呢?在我看來 nft 是一個標準化的箱子,我們在箱子裡要放東西進去,放什麼東西由使用者自己來定義,比如我放 token,放一個寶石,這個東西是不能被丟棄的,這樣的話就可以把各種資源統一封裝成 nft 展示出來。


我們提供了一個 nf**allery 的模組去存 nft。在以太坊裡轉讓 nft,只能提供 transfer 的方法,把 nft 從一個賬號轉到另一個賬號,指定 nft 的 id。但在以太坊裡沒法提供 withdraw,就是把這個 nft 作為一個物件拿出來,從使用者的 nf**allary 裡拿出來。拿出來能幹什麼呢?可以把nft封裝在另外一個結構體裡存下來,這樣 nft 之上的擴充套件能力和表達能力變得非常強。


為什麼能做到這點呢?就是剛才說的,nft 這個型別可以 store,但不能被 drop。程式語言和虛擬機器可以保證這個 nft 無論存到系統的哪裡,都不會憑空消失掉。當然還有可能有一種,不能 store 也不能drop 的型別,這種型別用來做什麼呢?這個大家可以思考下,後面講閃電貸的部分會講到。


來源:
https://starcoin.org/zh/developer/sips/sip-22/

程式碼文件,可在“閱讀原文”中複製


3、合約之間的依賴與呼叫

另外一個例子是合約間的依賴與呼叫。


來源:

https://github.com/openzeppelin/openzeppelin-contracts/blob/9b3710465583284b8c4c5d2245749246bb2e0094/contracts/token/erc20/ierc20.sol


我們看以太坊的例子,以太坊一般設計個 interface,透過 interface 對另外一個合約 address 進行呼叫。


它是怎麼轉換的?我的程式碼是在當前合約裡的,合約之間的呼叫是內部的交易,你可以理解成另外啟了一個虛擬機器,然後去執行另外一個合約的方法,然後再透過序列化和反序列化將結果傳遞回來。這個和傳統的 rpc 封裝實際上非常類似。為什麼這樣做呢?主要是因為安全隔離的問題,因為不同的合約實際上代表了不同的組織與機構。


這樣的呼叫方式其實有幾個難題:

比如,身份的傳遞。使用者呼叫了一個合約,然後合約內又呼叫了另外一個合約,那後面這個合約拿到的 sender 是誰呢?能不能拿到終端使用者?所以它提供了兩種方式,直接 call,sender 變成合約地址。


另外一個是 delegatecall 呼叫,這種呼叫會把原始的使用者資訊傳過去。但傳過去之後其實就牽扯另外一個難題,後面這個合約是惡意合約怎麼辦?


它現在的 delegated call 實現會把被呼叫合約的程式碼拿過來在呼叫方的上下文裡執行,它修改的狀態是當前合約的狀態,並不是被呼叫合約的狀態。這個很違反直覺。這是因為 solidity 沒有提供靜態呼叫方式,所以透過這種方式來模擬。以太坊上的很多安全的漏洞其實都來源於比較複雜的合約間的呼叫方式。


如果在 move 裡合約間是怎麼呼叫呢?一方面 move 裡的方法其實都相當於靜態方法,所以合約之間的依賴比較簡單。另外一方面,因為 move 的型別可以在合約間共享,可以透過型別共享來消除對動態呼叫的依賴。例如我剛才舉的 token 的例子:


我在銀行裡存了錢,然後我需要去商場裡買東西,我有兩種辦法付款,一種辦法是讓銀行授權給商場,它可以從我的賬號上扣錢,轉到商場在銀行的賬號上。銀行需要和商場協商呼叫的協議,這是一種方式,以太坊是這種方式。


另一種方式是現金的方式,我去銀行把現金提出來,到商場直接支付現金,商場可以存銀行,也可以存自己的保險箱裡。這樣銀行和商場之間兩個之間就不需協商互動協議,也不需要動態呼叫,只要都能識別"現金"這種型別就行。


這樣合約之間的依賴關係全部是靜態的方式,避免了上述的動態呼叫的安全問題。動態呼叫如果有多個層次的話,其實很難搞清楚它會不會產生重入,或者複雜的安全問題,甚至迴圈依賴。


來源:

https://github.com/starcoinorg/starcoin/blob/master/vm/stdlib/modules/treasury.move

程式碼文件,可在“閱讀原文”中複製


閃電貸的例子


看一看閃電貸的例子。閃電貸是我給人講 defi 的時候喜歡舉的例子。你給網際網路的或者其他外部朋友講 defi 的時候很難講清楚有什麼根本的不一樣。


你說什麼東西他說我們 cefi 也有,最後你給他講一個閃電貸他就舉不出來了,不可能在傳統金融裡找到一個閃電貸的例子。


閃電貸可以做到無抵押給任意人借無限的資金(上限是資金池),只要能在同一個交易中把資金還回來。


它充分利用了以太坊的動態呼叫特性。閃電貸提供一個入口方法,使用者需要在交易中呼叫該方法,並且透過引數傳遞自己實現的回撥合約(borrower)。使用者回撥合約中實現對貸款的支配邏輯(套利,清算等),以及還款邏輯。閃電貸合約先給使用者轉賬,再呼叫使用者的回撥合約,然後檢查自己的餘額,使用者是否還回來,如果沒換回來就報錯,導致交易失敗,整個交易的狀態回滾。


例子來源:

https://github.com/alcueca/erc3156/tree/main/contracts

程式碼文件,可在“閱讀原文”中複製


那 move 中不支援動態呼叫,如何實現這種特性呢?move 可以利用前面提到的 ability 特性, 宣告一個 flashloan 型別,這個型別沒有任何 ability,也就是既不能 store,也不能 copy,還不能 drop,它內部有一個欄位儲存 token。


flashloan 相當於一個箱子給裡面裝了 token,然後我直接給你。你可以把箱子裡面的錢拿出去花,但是這個這個箱子你不能 store,也不能 drop,你必須給我還回來,由我去銷燬掉。而我在銷燬的時候要檢查箱子裡有沒有把原始的 token 給我還回來,如果數不對就報錯。這樣,就不需代理也實現了閃電貸貸邏輯,還更靈活方便。


bewater

三、move 語言的創新點


我們總結一下,move 帶來的一些創新的點:


1)它有一套狀態操作協議,把狀態所有權明確了;

2)透過型別機制,實現了型別系統在不同的合約之間共享;

3)因為它的這種方式讓合約之間的依賴關係不一樣了。


可以透過靜態的方式依賴遠端的程式碼,可以實現更復雜的專案,更工程化。很難遇到合約太大了,沒法部署上去的問題。


我們從三個角度看程式語言的創新,合約的狀態機制,程式語言的特性,合約之間的依賴與呼叫。那有沒有其他的創新點?


程式語言本身特性上的擴充比較容易想到。比如說可見性。move 後來加了一種的可見性,叫 friend,類似於 java 裡的可見性 protect,module 之間可以用 friend 宣告方法的可見性,方便拆解 moudle ,構建更復雜的專案。

我隨便再開幾個腦洞,我最近也在琢磨的。網際網路時代一直想要的物件資料庫這種東西,實際上沒有用起來,關鍵是整個系統沒有把應用的狀態給接管了。但是在區塊鏈時代有沒有可能呢?能不能透過物件資料庫的方式來處理區塊鏈的狀態?物件資料庫跟區塊鏈解決狀態的方式是非常像的。


再比如說程式語言的繼承,結構體/類的繼承,如果放在智慧合約裡面表達出來什麼呢?最近大家玩的 nft 會發現 nft 有各種演化的玩法。比如有一個原始nft,後面衍生出不同的附加的特性,就很像是一種繼承的機制等玩法。


再比如能不能在程式中宣告所有權?比如說定義一個map,能不能規定裡 map 裡的某個 key 只能由某個賬號去操作?在 rust 裡所說的所有權是說程式方法對變數的所有權,而不是說使用者對某個資料的所有權。那能否把這套所有權的概念,跟外部的使用者影射起來在程式程式中表達和追蹤資料狀態的所有權?


最後總結一下,我覺得現在區塊鏈世界已經從 hodl 時代進入了 buidl 時代了。現在每天都有新的東西冒出來,有五花八門各種的想法值得我們去去構建去嘗試。而新的程式語言的出現和爆發標誌著開發者新的黃金時代的開啟(前一次是早期網際網路時代)。


我的分享到這裡,謝謝大家。


問答環節


提問1:王老師你覺得在不改變原有語言基礎上,加一些註釋,向 evm 新增屬性,你覺得是一個好的實現方法嗎?


王淵命:我最早的時候一直在想這個路線,如果不需要動 vm 的情況下,改程式語言是相對簡單的。


這裡面第一個問題是它的狀態,必須有辦法把狀態拆分出來。鏈跟狀態之間這套的處理機制現在沒有標準,傳統程式語言裡使用了作業系統標準,但是在鏈上還沒有一套統一的標準,狀態機制不一樣,靠語法糖是彌補不了的。


第二個是合約間的呼叫。如果想廢棄合約間的動態呼叫,實現合約之間靜態呼叫並且共享型別,要考慮安全性,其實非常難。需要在編譯期和執行時同時保證。所以從這個角度看,在以太坊上做這種嘗試成本太高了,萬一試驗失敗了對以太坊影響也太大了,不如在一個新的鏈上去嘗試新的東西。


提問2:為什麼要考慮用一門新的語言,因為我感覺到 move 語言各種改進,其實更多的像是對於鏈自身資料結構的修改,我理解類似於核心態的修改而已,不同核心態上面語言層的相容可以提供api的模式,就像您剛才也說的,比方說 msg.sender,它是可以感知到這些的。我們可以針對這些做一些修改,類似於提供新的api的形式給solidity。所以我們為什麼必須用move 呢?


王淵命:可以這樣改,例如把 sender 去擴充套件一些方法, sender.move_to, sender.move_from,讓開發者把狀態透過 sender.move_to 儲存,不要定義成合約的屬性,邏輯上講是可以的。但是這樣完全破壞了 solidity 本身的狀態處理機制了,相當於把它原來的狀態處理機制都廢棄了。


第二,這種方式只解決了狀態分散在不同使用者下的情況。但如果考慮到安全,其實這是個挺複雜的事情。比如如何控制 move_to/move_from 的許可權?我能不能在一個合約中透過 move_from 去讀另外一個合約中的型別?如果那樣可以安全就有問題了。


所以這套機制從安全性上來講,它要從程式語言原始碼層,到最後的 vm 的執行時一起來保障,不然的話其實很難保障安全性。


(文章來源:bewater community)


免責聲明:

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

推荐阅读

;