萬維大講堂 | 如何在萬維鏈上打造可升級智能合約

萬維大講堂 | 如何在萬維鏈上打造可升級智能合約

免責聲明:本文旨在傳遞更多市場信息,不構成任何投資建議。文章僅代表作者觀點,不代表火星財經官方立場。

小編:記得關注哦

來源:萬維鏈Wanchain

寫在前面

Wanchain作為一條著力打造分佈式金融基礎設施的跨鏈公鏈,在跨鏈協議、跨鏈標準制定、PoS機制研究、隨機數生成、dApp應用場景、行業解決方案、合約設計模式等諸多方面,萬維鏈團隊有著紮實的理論基礎和豐富的實踐經驗。秉承兼容幷包的區塊鏈精神,Wanchain團隊開辦“萬維區塊鏈知識大講堂”欄目,憑藉團隊的技術積澱,由團隊技術大咖親自執筆,定期為整個行業貢獻有趣、有價值、有意義的內容輸出,為業內業外的普通用戶和專業用戶拋磚引玉,讓萬維鏈和社區全體成員、和整個行業共同進步。

萬維區塊鏈知識大講堂之智能合約篇:如何在Wanchain上打造可升級智能合約?由Wanchain技術團隊研發總監Gabriel Guo為大家徐徐道來如何創建數據和邏輯分離、數據結構可擴展的可升級智能合約。

正文

一、合約升級面臨的問題

Wanchain的智能合約使用的是solidity語言,用過solidity語言的人都知道,如果智能合約有bug或者需要擴展新的特性,將是一個巨大的挑戰。源碼本身的修改程序員都能搞定,最大的挑戰是solidity每次部署合約後,合約地址都會改變,這樣就面臨很多棘手的問題:

  • 所有的用到了該合約的DApps都需要修改合約地址來適配新的合約

  • 鏈上合約裡的數據要遷移到新的合約裡面,一般會對舊合約做快照,然後把數據導入到新合約中。這種方式的不足之處在於工作量大,需要腳本掃鏈,很容易出錯。一旦出錯,後果可能是無法承擔的

因此,程序員就自然而然的思考能不能打造一種可升級的智能合約架構呢?答案是肯定的,本文將詳細闡述如何做到這一點。

二、合約數據與邏輯分離的方法及技巧

要打造可升級的智能合約架構,就必須做到數據和邏輯分離。數據保存在一個合約裡面,該合約保持穩定,避免升級;邏輯保存在另外一個合約裡面,該合約可以升級。畢竟每次升級都是修改的代碼邏輯。這樣的設計,正好完美的解決了上面提到的第二個痛點。

萬維大講堂 | 如何在萬維鏈上打造可升級智能合約

圖 1 合約數據和邏輯分離示意圖

如圖 1所示,Data合約和Logical合約A開發完成,部署到鏈上,Logical合約A需要用到的數據都通過合約間調用存儲在Data合約裡面。兩個合約形成了一個整體服務。DApps只需要調用Logical合約A的ABI(應用程序二進制接口)。突然有一天發現了嚴重的bug,於是修復完bug後,部署Logical合約B到鏈上,修改DApps指向新的Logical合約B,從而達到了升級合約的目的,完美的避免了數據的遷移。

有細心的程序員這時候會提出質疑,上述方法雖然能解決數據遷移的痛點,但是要修改所有使用該智能合約的DApps,還是非常的麻煩。那麼彆著急,下面介紹一種更高級的數據和邏輯分離的方法,同時解決上述的兩個痛點。

在開始介紹新的方法之前,要引入一種solidity裡面的概念:delegate call。Solidity提供了幾種合約之間調用的方法,最常用的是call,大部分合約都是使用的call。現在說的是不太常見的delegatecall,這裡只是簡單的說明一下call和delegatecall的區別,詳細的介紹請大家自行Google。

萬維大講堂 | 如何在萬維鏈上打造可升級智能合約

圖 2 call流程圖

萬維大講堂 | 如何在萬維鏈上打造可升級智能合約

圖 3 delegatecall流程圖

如圖 2所示,是典型的call調用,重點關注兩個合約的上下文。Caller合約的msg.sender和msg.value都是用戶(EOA)發送的交易中的數據。Target合約的上下文變了,msg.sender和msg.value變成了Caller合約的交易數據,不再是用戶(EOA)的了。這個還是很好理解的。

我們再看圖 3,Caller合約跟圖 2沒有差別,但Target的msg.sender和msg.value居然還是用戶(EOA)的交易數據,很神奇。這就是delegatecall跟call的本質區別,delegatecall並不會切換合約的上下文,也就是Target合約使用的是Caller的存儲空間,修改的也是Caller的存儲空間。

萬維大講堂 | 如何在萬維鏈上打造可升級智能合約

圖 4 代理合約和邏輯合約示意圖

言歸正傳,我們回到如何打造可升級智能合約這個話題上來。如圖 4所示,我們使用一個Proxy合約來存儲數據,Proxy合約使用delegatecall去調用Logical合約A。這樣一來,Logical合約A讀取和寫入的數據都是在Proxy合約裡面。升級合約也變得簡單起來。部署完Logical合約B後,只需要發一條交易到Proxy,修改Proxy指向新的Logical合約B即可。此升級過程對DApps透明,DApps完全感知不到合約進行了升級,於是完美解決了本文開頭提到的兩個痛點,既不要升級DApps,也不需要進行數據遷移。

這個時候,挑剔的程序員可能會提出新的挑戰,如果要新增特性,需要修改數據結構怎麼辦?上面說到的只是修改邏輯,而現實中修改數據結構也是很常見的需求。這個也是有辦法解決的,就是在設計數據結構的時候全部用Key-Value的Map數據結構,這種結構的好處是原則上可以無限擴展。當需要保存新的數據,只要重新定義一個新的Key就可以了。這樣做的代價就是代碼的可讀性下降不少,本來一些可以用結構體很容易做到的,卻要生生拆成Key-Value的Map數據。所以我的建議是可以使用折中方案,就是開發第一版的時候還是使用常見的數據結構,可讀性好,開發週期也短一些。但同時定義一些常見類型的Key-Value的Map用於擴展。當升級合約的時候需要新增數據,就使用這些預留好的Map去擴展,畢竟這樣的需求並不是那麼常見。

最後需要提醒一下程序員,使用Proxy-Delegate的架構實現可升級智能合約,是有約束條件的,就是Proxy和Delegate合約的狀態變量的定義要保持一致,順序也要保持一致。切記切記,否則出現問題是很難調試的。

三、Wanchain智能合約實例

到此為止,相信程序員已經知道如何去設計自己的可升級solidity智能合約了。但還是需要實踐才能領悟得更深刻。Wanchain的EOS跨鏈智能合約用的就是Proxy-Delegate架構,可以作為很好的參考。

https://github.com/wanchain/wanchain-crosschain-contracts/tree/eos-wan-cross/eos-wan-cross/solidity/contracts

四、Proxy合約模板

<code>pragma solidity ^

0.4

.24

;/<code>
<code>contract

Proxy

{/<code><code>address public owner;/<code><code> event Upgraded(address indexed implementation);/<code><code> address internal _implementation;/<code>
<code>

constructor

public {/<code><code> owner = msg.sender;/<code><code>}/<code>
<code> modifier onlyOwner {/<code><code>

require

(msg.sender == owner,

"Not owner"

);/<code><code> _;/<code><code>}/<code><code>

function

implementation

public

view

returns

(

address

)

{/<code><code>

return

_implementation;/<code><code>}/<code>
<code>

function

upgradeTo

(

address impl

)

public

onlyOwner

{/<code><code>

require

(impl != address(

0

),

"Cannot upgrade to invalid address"

);/<code><code>

require

(impl != _implementation,

"Cannot upgrade to the same implementation"

);/<code><code> _implementation = impl;/<code><code> emit Upgraded(impl);/<code><code>}/<code>
<code>

function

external

payable

{/<code><code> address _impl = _implementation;/<code><code>

require

(_impl != address(

0

),

"implementation contract not set"

);/<code>
<code> assembly {/<code><code>

let

ptr := mload(

0x40

)/<code><code> calldatacopy(ptr,

0

, calldatasize)/<code><code>

let

result := delegatecall(gas, _impl, ptr, calldatasize,

0

,

0

)/<code><code>

let

size := returndatasize/<code><code> returndatacopy(ptr,

0

, size)/<code>
<code>

switch

result/<code><code>

case

0

{ revert(ptr, size) }/<code><code>

default

{

return

(ptr, size) }/<code><code> }/<code><code> }/<code><code>}/<code>

關於Wanchain

Wanchain,中文名萬維鏈,聚焦於跨鏈機制的研發,通過構建具有跨鏈能力的分佈式金融基礎設施來實現萬鏈互聯的宏偉目標。截至目前,Wanchain已成功跨鏈集成比特幣、以太坊、EOS以及以太坊和EOS上的生態代幣,並設計提出了通用跨鏈框架T-Bridge,旨在實現資產和數據在不同公鏈和聯盟鏈間的自由流轉。在共識機制方面,Wanchain設計並上線了擁有完整委託機制的實用PoS共識協議,即星系共識。不論是跨鏈機制,還是共識協議,Wanchain一直處於行業的領先位置。

掃描關注微信公眾號萬維鏈(id:WanchainCN),獲取更多信息


分享到:


相關文章: