浅谈以太坊智能合约的设计模式与升级方法
- 淺談以太坊智能合約的設(shè)計(jì)模式與升級(jí)方法
- 1. 最佳實(shí)踐
- 2. 實(shí)用設(shè)計(jì)案例
- 2.1 控制器合約與數(shù)據(jù)合約: 1->1
- 2.2 控制器合約與數(shù)據(jù)合約: 1->N
- 2.3 控制器合約與數(shù)據(jù)合約: N->1
- 2.4 控制器合約與數(shù)據(jù)合約: N->N
- 2.5 總結(jié)
- 3. 升級(jí)
- 3.1 控制器合約升級(jí),數(shù)據(jù)合約不升級(jí)
- 3.2 控制器合約不升級(jí),數(shù)據(jù)合約升級(jí)
- 3.3 控制器合約升級(jí),數(shù)據(jù)合約升級(jí)
- 4. 數(shù)據(jù)遷移
- 4.1 硬編碼遷移法
- 4.2 硬拷貝遷移法
- 4.3 默克爾樹(shù)遷移法
淺談以太坊智能合約的設(shè)計(jì)模式與升級(jí)方法
以太坊EVM是當(dāng)前區(qū)塊鏈行業(yè)應(yīng)用最為廣泛的虛擬機(jī)。其所支持的智能合約語(yǔ)言是圖靈完備的。該語(yǔ)言支持各種基礎(chǔ)類(lèi)型(Booleans,Integers,Address,String,Enum,Address等)、復(fù)雜類(lèi)型(Struct,Mapping,Array等)、復(fù)雜的表達(dá)式和控制結(jié)構(gòu)及接口繼承等面向?qū)ο蟮奶匦浴?/p>
正是由于強(qiáng)大的智能合約語(yǔ)言,原本在真實(shí)世界中的復(fù)雜商業(yè)邏輯和應(yīng)用都能在區(qū)塊鏈上輕松實(shí)現(xiàn)。然而需要注意的是,盡管公有鏈可以實(shí)現(xiàn)合理的GAS機(jī)制自我保護(hù),聯(lián)盟鏈可以用其他機(jī)制替代GAS的計(jì)算及代幣化來(lái)保障EVM沙盒安全,但由于區(qū)塊鏈運(yùn)行機(jī)制的原因,智能合約的運(yùn)行即使是異常運(yùn)行都會(huì)在所有區(qū)塊鏈節(jié)點(diǎn)上獨(dú)立重復(fù)運(yùn)行。因此,無(wú)論是在公有鏈還是聯(lián)盟鏈運(yùn)行智能合約都是非常昂貴(運(yùn)算資源、存儲(chǔ)資源)的操作。
另外,智能合約與傳統(tǒng)應(yīng)用程序有一個(gè)不同的地方在于智能合約一經(jīng)發(fā)布于區(qū)塊鏈上就無(wú)法篡改,即使智能合約中有Bug需要修復(fù)或者業(yè)務(wù)邏輯變更,它也不能直接在原有的合約上直接修改再重新發(fā)布。因此在設(shè)計(jì)之初就需要結(jié)合業(yè)務(wù)場(chǎng)景考慮合理的升級(jí)機(jī)制。
總而言之,智能合約實(shí)現(xiàn)上要達(dá)到的目標(biāo)是:完備的業(yè)務(wù)功能、精悍的代碼邏輯、良好的模塊抽象、清晰的合約結(jié)構(gòu)、合理的安全檢查、完備的升級(jí)方案。
智能合約的生命周期主要有設(shè)計(jì)、開(kāi)發(fā)、部署、運(yùn)行、升級(jí)、銷(xiāo)毀。在下文中主要是基于目標(biāo)在設(shè)計(jì)階段、升級(jí)階段的一些梳理總結(jié)。
1. 最佳實(shí)踐
從業(yè)務(wù)視角來(lái)看,智能合約只需要做兩件事,其一是如何定義數(shù)據(jù)的結(jié)構(gòu)和讀寫(xiě)方式,其二是如何處理數(shù)據(jù)并對(duì)外提供服務(wù)接口。
為了更好的做好模塊抽象和合約結(jié)構(gòu)分層,將這兩件事分開(kāi),既是將業(yè)務(wù)控制邏輯和數(shù)據(jù)從合約代碼層面就做好分離,這樣的處理在復(fù)雜業(yè)務(wù)邏輯場(chǎng)景中經(jīng)過(guò)實(shí)踐是當(dāng)前被認(rèn)為最佳的模式。
這個(gè)模式簡(jiǎn)稱(chēng)為CD(Controller-Data)模式。將合約分為兩類(lèi):控制器合約(Controller Contract)與數(shù)據(jù)合約(Data Contract)。
控制器合約通過(guò)訪(fǎng)問(wèn)數(shù)據(jù)合約獲得數(shù)據(jù),并對(duì)數(shù)據(jù)做邏輯處理,然后寫(xiě)回?cái)?shù)據(jù)合約。�它專(zhuān)注于對(duì)數(shù)據(jù)的邏輯處理和對(duì)外提供服務(wù)。根據(jù)處理邏輯的不同,常見(jiàn)的有命名空間控制器合約、代理控制器合約、業(yè)務(wù)控制器合約、工廠(chǎng)控制器合約等。一般情況下,控制器合約不需要存儲(chǔ)任何數(shù)據(jù),它完全依賴(lài)外部的輸入來(lái)決定對(duì)數(shù)據(jù)合約的訪(fǎng)問(wèn)。特殊情況下,控制器合約可以存儲(chǔ)某個(gè)固定的數(shù)據(jù)合約的地址或者命名空間(通過(guò)命名空間在運(yùn)行時(shí)獲得合約地址)。
數(shù)據(jù)合約專(zhuān)注于數(shù)據(jù)結(jié)構(gòu)定義與所存儲(chǔ)數(shù)據(jù)的讀寫(xiě)裸接口。為了達(dá)到數(shù)據(jù)統(tǒng)一訪(fǎng)問(wèn)管理和數(shù)據(jù)訪(fǎng)問(wèn)權(quán)限控制的目的,最好是將數(shù)據(jù)讀寫(xiě)接口只暴露給對(duì)應(yīng)的控制器合約。禁止其他方式的讀寫(xiě)訪(fǎng)問(wèn)。
基于這個(gè)模式,遵循從上至下的分析方式,從對(duì)外提供的服務(wù)接口開(kāi)始設(shè)計(jì)各類(lèi)控制器合約,再逐步過(guò)渡到服務(wù)接口所需要的數(shù)據(jù)模型和存儲(chǔ)方式,進(jìn)而設(shè)計(jì)各類(lèi)數(shù)據(jù)合約,可以較為快速的完成合約架構(gòu)的設(shè)計(jì)。
2. 實(shí)用設(shè)計(jì)案例
在CD模式下,根據(jù)控制器合約與數(shù)據(jù)合約之間的操作關(guān)系,從邏輯上歸結(jié)為四類(lèi):
假設(shè)一個(gè)業(yè)務(wù)場(chǎng)景:將全國(guó)所有銀行的業(yè)務(wù)和信息上鏈。
2.1 控制器合約與數(shù)據(jù)合約: 1->1
假設(shè)全國(guó)只有兩家銀行,A銀行和B銀行。A銀行只有存款業(yè)務(wù),B銀行只有取款業(yè)務(wù)。一種可能的設(shè)計(jì)是這樣的:
代理控制器合約:面向Dapp,是所有業(yè)務(wù)合約的入口,提供命名空間服務(wù),提供了命名空間到合約地址的映射。使得Dapp對(duì)鏈上合約升級(jí)導(dǎo)致的地址變更無(wú)感知。例如,Dapp對(duì)A銀行的存款請(qǐng)求只需要(“BankA",deposit,args) 即可。對(duì)B銀行的取款請(qǐng)求只需要(”BankB",withdraw,args)即可。代理器控制合約實(shí)現(xiàn)上應(yīng)該是區(qū)塊鏈底層內(nèi)置的、固化的,或者是業(yè)務(wù)上極少變更的。Dapp在業(yè)務(wù)運(yùn)行之前已經(jīng)明確知道代理控制器合約的地址。
命名控制器合約:面向鏈上合約,提供命名空間服務(wù),提供了命名空間到合約地址的映射。使得鏈上合約可以在運(yùn)行時(shí)根據(jù)命名獲得實(shí)際的合約地址。例如,A銀行控制器合約向命名控制器合約請(qǐng)求(“BankA-Data"),可以獲得A銀行數(shù)據(jù)合約地址,使得A銀行控制器合約可以在運(yùn)行時(shí)訪(fǎng)問(wèn)A銀行數(shù)據(jù)合約。它與代理控制器合約的主要不同在于服務(wù)對(duì)象的不同,代理控制器合約面向Dapp,命名控制器合約面向鏈上合約。另外,命名控制器合約包含有版本控制的設(shè)計(jì)(下文第3.2節(jié)介紹),可以根據(jù)需要配合灰度策略的實(shí)施。
A銀行控制器合約:提供了存款服務(wù)接口deposit。部署初始化時(shí)已經(jīng)明確知道自己的身份”BankA"。并且可以在運(yùn)行時(shí)通過(guò)命名控制合約獲得”BankA“的數(shù)據(jù)合約“BankA-Data"的地址。
A銀行數(shù)據(jù)合約:保存了A銀行的當(dāng)前余額。提供add和sub接口給A銀行控制器合約來(lái)更新余額信息。
B銀行控制器合約:提供了存款服務(wù)接口withdraw。部署初始化時(shí)已經(jīng)明確知道自己的身份”BankB"。并且可以在運(yùn)行時(shí)通過(guò)命名控制合約獲得”BankB“的數(shù)據(jù)合約"BankB-Data"的地址。
B銀行數(shù)據(jù)合約:保存了B銀行的當(dāng)前余額。提供add和sub接口給B銀行控制器合約來(lái)更新余額信息。
對(duì)A銀行的存款請(qǐng)求的流程是這樣的:
2.2 控制器合約與數(shù)據(jù)合約: 1->N
假設(shè)全國(guó)有N家銀行,所有銀行都有存款業(yè)務(wù)和取款業(yè)務(wù),并且業(yè)務(wù)流程都是一樣的。一種可能的設(shè)計(jì)是這樣的:
這個(gè)設(shè)計(jì)與上面的2.1不一樣的地方在于,將存款服務(wù)接口和取款接口都集中歸結(jié)到銀行業(yè)務(wù)控制器合約里面了。這意味著任何銀行的存款和取款業(yè)務(wù)都由銀行業(yè)務(wù)控制器合約來(lái)統(tǒng)一處理,處理邏輯上不再區(qū)分是A銀行還是B銀行,只是在數(shù)據(jù)訪(fǎng)問(wèn)的時(shí)候需要根據(jù)入?yún)⒌牟煌瑏?lái)決定訪(fǎng)問(wèn)不同的銀行數(shù)據(jù)合約。
還有,于2.1相比,對(duì)于Dapp而言,它發(fā)出請(qǐng)求的時(shí)候只需要將請(qǐng)求發(fā)往固定的”Bank"就可以了,不用具體關(guān)心某個(gè)銀行。
另外,由于銀行有很多個(gè),并且它們的存儲(chǔ)結(jié)構(gòu)都是一樣的,因此可以設(shè)計(jì)一個(gè)銀行數(shù)據(jù)合約的工廠(chǎng)控制器合約,來(lái)負(fù)責(zé)對(duì)新的數(shù)據(jù)合約的生成實(shí)現(xiàn)模板化。
對(duì)A銀行的存款請(qǐng)求的流程是這樣的:
2.3 控制器合約與數(shù)據(jù)合約: N->1
假設(shè)全國(guó)有N家銀行,所有銀行都有存款業(yè)務(wù)和取款業(yè)務(wù),并且業(yè)務(wù)流程都是一樣的,但是由于業(yè)務(wù)邏輯較為復(fù)雜,出于模塊化維護(hù)的需要,需要將存款業(yè)務(wù)和取款業(yè)務(wù)做分拆。一種可能的設(shè)計(jì)是這樣的:
這個(gè)設(shè)計(jì)與上面的2.2不一樣的地方在于,將存款服務(wù)接口和取款接口拆分到了不同的業(yè)務(wù)控制器合約里面了。這意味著不同的業(yè)務(wù)邏輯從模塊上做了清晰的切分。對(duì)于Dapp而言,它發(fā)出請(qǐng)求的時(shí)候需要明確指向所對(duì)應(yīng)的業(yè)務(wù)接口。
對(duì)A銀行的存款請(qǐng)求的流程是這樣的:
2.4 控制器合約與數(shù)據(jù)合約: N->N
此類(lèi)情況可以拆解為上面三種情況的組合。不再贅述。
2.5 總結(jié)
從Dapp視角考慮,可以總結(jié)如下:
| 1->1 | 面向業(yè)務(wù)對(duì)象 |
| 1->N | 面向業(yè)務(wù)流程 |
| N->1 | 面向業(yè)務(wù)接口 |
| N->N | / |
3. 升級(jí)
在CD模式下,在業(yè)務(wù)邏輯變更需要升級(jí)合約的情況下,根據(jù)控制器合約與數(shù)據(jù)合約的升級(jí)關(guān)系來(lái)劃分,可以歸納為以下三種情況:
| 升級(jí) | 不升級(jí) |
| 不升級(jí) | 升級(jí) |
| 升級(jí) | 升級(jí) |
在升級(jí)過(guò)程中,還需要考慮是全量升級(jí)還是灰度升級(jí)?如果是灰度升級(jí),灰度策略是怎么樣的?另外,在多鏈場(chǎng)景和單鏈場(chǎng)景、跨鏈場(chǎng)景,升級(jí)過(guò)程是否有不同?多鏈場(chǎng)景的灰度策略如何考慮?新舊版本數(shù)據(jù)能否共存?如果需要數(shù)據(jù)遷移,如何做到無(wú)縫遷移?
下面以最為常見(jiàn)的1->N 場(chǎng)景來(lái)介紹不同的升級(jí)情況。
3.1 控制器合約升級(jí),數(shù)據(jù)合約不升級(jí)
如上圖所示,銀行業(yè)務(wù)控制器合約從V1升級(jí)到V2,而其他的合約和接口都是不需要更新的,假設(shè)V2版本相對(duì)V1版本只是升級(jí)withdraw這個(gè)接口。
此時(shí),V2版本的銀行業(yè)務(wù)控制器合約需要做的事情是:
完成V2版本的合約工作之后,即可發(fā)布一個(gè)普通交易,交易中的邏輯是,先部署V2版本的銀行業(yè)務(wù)控制器合約,再將其地址更新到代理控制器合約中,使得將“Bank”映射到V2版本的合約地址上。這樣控制器合約即升級(jí)完成。
如果需要回退版本,只需要發(fā)布一個(gè)普通交易,將代理控制器合約的“Bank”映射到V1版本的合約地址上即可。
以上是單鏈場(chǎng)景的升級(jí)方法。如果是多鏈場(chǎng)景,只需根據(jù)業(yè)務(wù)的需要來(lái)判斷鏈與鏈之間的灰度策略,重復(fù)單鏈場(chǎng)景的升級(jí)即可。如果是跨鏈場(chǎng)景,需要根據(jù)跨鏈兩端的具體情況來(lái)制定升級(jí)方法。
而對(duì)于業(yè)務(wù)發(fā)起端的Dapp而言,它是無(wú)任何感知的。它對(duì)A銀行的存款請(qǐng)求與2.2中完全一樣。依舊是以(“Bank",deposit,”BankA“,money)來(lái)發(fā)出請(qǐng)求。
總結(jié)而言,灰度策略定義在新版本的控制器合約中,數(shù)據(jù)無(wú)需遷移,業(yè)務(wù)無(wú)感知,無(wú)需停止服務(wù)。無(wú)縫升級(jí)。
3.2 控制器合約不升級(jí),數(shù)據(jù)合約升級(jí)
如上圖所示,A銀行數(shù)據(jù)合約從V1升級(jí)到V2。而其他的合約和接口都是不需要更新,假設(shè)V2版本相對(duì)V1版本只是增加新的數(shù)據(jù)字段loan,并假設(shè)銀行業(yè)務(wù)控制器合約原本就能支持到V2版本的A銀行數(shù)據(jù)合約(如果是銀行業(yè)務(wù)控制器合約也需要升級(jí)則是3.3節(jié)的場(chǎng)景,這里不做描述)。
此時(shí),V2版本的A銀行數(shù)據(jù)合約需要做的事情是:
需要注意的是,命名控制器合約有如下重要的設(shè)計(jì):
因此,完成V2版本的數(shù)據(jù)合約之后,即可發(fā)布一個(gè)普通交易,交易中的邏輯是,先部署V2版本的A銀行數(shù)據(jù)合約,并完成V1版本數(shù)據(jù)合約到V2版本數(shù)據(jù)合約的數(shù)據(jù)遷移(數(shù)據(jù)遷移方法第4節(jié)會(huì)描述),接著將V2版本數(shù)據(jù)合約地址注冊(cè)到命名控制器合約,并更新BankA-Data所映射的當(dāng)前有效verison=V2。此時(shí)已完成了A銀行數(shù)據(jù)合約的V2版本升級(jí)。
如果需要回退版本,只需要發(fā)布一個(gè)普通交易,將命名控制器合約的BankA-Data所映射的當(dāng)前有效verison=V1即可。
而對(duì)于業(yè)務(wù)發(fā)起端的Dapp而言,它是無(wú)任何感知的。它對(duì)A銀行的存款請(qǐng)求與2.2中完全一樣。依舊是以(“Bank",deposit,”BankA“,money)來(lái)發(fā)出請(qǐng)求。
對(duì)于B銀行而言,因?yàn)锽銀行數(shù)據(jù)合約并沒(méi)有執(zhí)行升級(jí),所以與它相關(guān)的業(yè)務(wù)請(qǐng)求依然是訪(fǎng)問(wèn)的B銀行數(shù)據(jù)合約的V1版本。所以,對(duì)于歷史舊版本的數(shù)據(jù)合約,可以根據(jù)業(yè)務(wù)的需要來(lái)判斷是否需要對(duì)歷史舊版本執(zhí)行升級(jí)。有些特殊場(chǎng)景下,需要對(duì)所有的歷史舊版本數(shù)據(jù)合約進(jìn)行升級(jí),這時(shí)可以利用命名控制器合約的遍歷功能,對(duì)所有數(shù)據(jù)合約進(jìn)行類(lèi)似的升級(jí)。而對(duì)于新加入的C銀行,它可以直接使用最新版本V2的數(shù)據(jù)合約,按照正常流程完成部署與注冊(cè),無(wú)任何額外操作。
正是由于有了命名控制器合約的版本控制邏輯,可以使得即使存在新老版本數(shù)據(jù)合約并存的情況下,業(yè)務(wù)控制器類(lèi)合約依然能正常運(yùn)行。而對(duì)于由于業(yè)務(wù)的發(fā)展和不斷的版本升級(jí),會(huì)帶來(lái)命名數(shù)據(jù)合約的存儲(chǔ)量膨脹,導(dǎo)致可能出現(xiàn)的性能下降的情況,依然可以套用本節(jié)所述的數(shù)據(jù)遷移與升級(jí)的方法來(lái)解決。
以上是單鏈場(chǎng)景的升級(jí)方法。如果是多鏈場(chǎng)景,只需根據(jù)業(yè)務(wù)的需要來(lái)判斷鏈與鏈之間的灰度策略,重復(fù)單鏈場(chǎng)景的升級(jí)即可。如果是跨鏈場(chǎng)景,需要根據(jù)跨鏈兩端的具體情況來(lái)制定升級(jí)方法。
總結(jié)而言,得益于命名控制器合約的版本控制設(shè)計(jì),灰度策略可以交給業(yè)務(wù)方非常自由地選擇,業(yè)務(wù)無(wú)感知,無(wú)需停止服務(wù)。無(wú)縫升級(jí)。
3.3 控制器合約升級(jí),數(shù)據(jù)合約升級(jí)
此種情況下,實(shí)質(zhì)是3.1與3.2 兩種情況的混搭。
因此根據(jù)具體情況,拆解成參考3.1和3.2場(chǎng)景方法來(lái)執(zhí)行即可。
4. 數(shù)據(jù)遷移
如3.2節(jié)所描述,在數(shù)據(jù)合約升級(jí)的場(chǎng)景,某些情況需要處理歷史數(shù)據(jù)在新舊合約之間的遷移。遷移的方法有如下三種,各有特點(diǎn)。
4.1 硬編碼遷移法
硬編碼遷移法指的是,新版本的數(shù)據(jù)合約中保存一個(gè)指向舊版本數(shù)據(jù)合約的合約地址,新版本數(shù)據(jù)合約保存的是增量的數(shù)據(jù)內(nèi)容。
這樣相當(dāng)于新版本合約保留了一份舊版本數(shù)據(jù)的指針,當(dāng)新版本需要使用舊數(shù)據(jù)的時(shí)候,直接調(diào)用舊數(shù)據(jù)合約地址對(duì)應(yīng)數(shù)據(jù)接口即可。這樣,新舊版本數(shù)據(jù)合約可以并存,即使是在異常情況下,數(shù)據(jù)被誤寫(xiě)到了舊版本合約上,它依然可以被新版本所訪(fǎng)問(wèn)到。
這個(gè)方法的優(yōu)點(diǎn)是:新舊合約可以同時(shí)并存,不增加區(qū)塊鏈存儲(chǔ)壓力,簡(jiǎn)單靈活,較強(qiáng)的升級(jí)容錯(cuò)能力。缺點(diǎn):持續(xù)不斷的版本升級(jí)會(huì)導(dǎo)致形成較長(zhǎng)的鏈?zhǔn)竭壿嬯P(guān)系,維護(hù)成本較高。
4.2 硬拷貝遷移法
硬拷貝遷移法指的是,新版本和舊版本之間切斷邏輯關(guān)系,利用外部遷移工具,將舊版本數(shù)據(jù)逐步拷貝到鏈下,再?gòu)逆溝轮匦麓鎯?chǔ)到新版本合約的過(guò)程。
這個(gè)方法的優(yōu)點(diǎn)是:無(wú)歷史包袱。缺點(diǎn)是:大幅度增加區(qū)塊鏈存儲(chǔ)壓力;數(shù)據(jù)遷移工具需要適配不同的數(shù)據(jù)合約,開(kāi)發(fā)成本較高;遷移過(guò)程需要停止服務(wù),否則容易出現(xiàn)臟數(shù)據(jù);數(shù)據(jù)量大時(shí),耗時(shí)長(zhǎng),操作復(fù)雜,容易出錯(cuò),基本無(wú)法實(shí)操。
4.3 默克爾樹(shù)遷移法
默克爾數(shù)遷移法要點(diǎn)如下:
這個(gè)方法擁有前面兩個(gè)方法的所有優(yōu)點(diǎn),且簡(jiǎn)單高效,安全,實(shí)操性強(qiáng)。缺點(diǎn):需要區(qū)塊鏈底層功能特性的支持。
https://github.com/toxotguo/thinking/blob/master/%E6%B5%85%E8%B0%88%E4%BB%A5%E5%A4%AA%E5%9D%8A%E6%99%BA%E8%83%BD%E5%90%88%E7%BA%A6%E7%9A%84%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E4%B8%8E%E5%8D%87%E7%BA%A7%E6%96%B9%E6%B3%95.md
總結(jié)
以上是生活随笔為你收集整理的浅谈以太坊智能合约的设计模式与升级方法的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 区块链技术:智能合约入门
- 下一篇: 跨链资产原子转移工具包 Decred a