国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

智能合約升級模式介紹 — 入門篇

YFan / 1418人閱讀

摘要:以后的邏輯合約可以升級現(xiàn)有的方法或者創(chuàng)造新的方法,但是不能引入新的狀態(tài)變量。使用非結(jié)構(gòu)化存儲升級非結(jié)構(gòu)化存儲模式和繼承存儲類似,但是不要求邏輯合約繼承任何和升級相關(guān)的狀態(tài)變量。

以太坊最大的優(yōu)勢就是,每一筆用來轉(zhuǎn)賬、部署合約或者和合約交互的交易(事務(wù))都被存在一個叫做區(qū)塊鏈的公共賬本上。一旦交易發(fā)生,就再也無法隱藏或者改變。這帶來一個巨大的好處,就是在以太坊中的每一個節(jié)點都可以去驗證任意一筆交易的合法性和當(dāng)前狀態(tài)。這使得以太坊成為一個非常健壯的去中心化系統(tǒng)。

但是隨之而來的是,它還有一個最大的缺點,就是智能合約一旦部署之后,就再也無法改變源碼。開發(fā)中心化應(yīng)用(比如facebook或者Airbnb)的開發(fā)者,都已經(jīng)習(xí)慣了,為了修復(fù)bug或者引入新的特性而頻繁更新產(chǎn)品。但這種方式卻不適用以太坊。

還記得當(dāng)面Parity多簽名錢包被黑導(dǎo)致150000以太幣被偷的惡劣事件嗎?在整個攻擊中,就因為錢包中的一個bug導(dǎo)致很多巨額錢包的資金被清空。而唯一的解決方案就是嘗試以比黑客更快的速度,利用相同的漏洞攻擊剩余的錢包,來把以太幣重新分配給它們合法的所有者。

要是有一種方法可以在智能合約部署之后,還能對它們進行升級,那該多好...

引入代理模式

盡管想升級已經(jīng)部署的智能合約中的代碼是不可能的,但是可以通過設(shè)計一個代理合約結(jié)構(gòu),這個結(jié)構(gòu)可以讓你可以通過新部署一個合約的方式,來實現(xiàn)升級主要的處理邏輯的目的。

代理結(jié)構(gòu)模式就像下面這張圖一樣:所有消息通過一個代理合約來間接調(diào)用最新部署的邏輯合約。如果想要升級的話,只需要部署一個新的合約,然后在代理合約中更新引用新的合約地址就可以了。

作為實現(xiàn)zeppelin_os的一部分,zeppelin正致力于實現(xiàn)集中代理模式。目前已經(jīng)探索出來的有下面三個:

繼承存儲模式 Inherited Storage

永久存儲模式 Eternal Storage

非結(jié)構(gòu)化存儲模式 Unstructured Storage

所有三種模式都依賴低階的delegatecall。盡管solidity提供了一個delegatecall方法,但它只能返回true或者false來顯示調(diào)用是否成功,而不是允許你操作返回的數(shù)據(jù)。

在我們深入了解之前,理解兩個關(guān)鍵的概念很重要:

當(dāng)調(diào)用一個合約中并不支持的的方法時,就會調(diào)用合約中的fallback方法。你可以自己寫一個fallback函數(shù)來處理這種場景。代理合約就是用自定義的fallback方法將調(diào)用重定向到其他合約實現(xiàn)。

每當(dāng)合約A授權(quán)對另一個合約B的調(diào)用時,它就會在合約A的上下文中執(zhí)行合約B的代碼。這就意味著msg.valuemsg.sender的值會被保留。并且對存儲的修改將會作用在合約A的存儲上。

zeppelin的代理合約,為了可以返回調(diào)用邏輯合約后的結(jié)果,實現(xiàn)了自己的delegatecall方法,所有模式都是這樣。如果你想要使用zeppelin的代理合約代碼,你就要理解代碼的每一個細節(jié)。讓我們先來看看它是如何發(fā)揮作用的,以及理解為了達到目的它所使用的assembly操作碼。

 assembly {
    let ptr := mload(0x40)
    calldatacopy(ptr, 0, calldatasize)
    let result := delegatecall(gas, _impl, ptr, calldatasize, 0, 0)
    let size := returndatasize
    returndatacopy(ptr, 0, size)

    switch result
    case 0 { revert(ptr, size) }
    default { return(ptr, size) }
 }

為了授權(quán)對另一個合約中方法的調(diào)用,我們需要把它賦值給proxy合約接收的msg.data。因為msg.databytes類型的,是一個動態(tài)的數(shù)據(jù)結(jié)構(gòu),所以它在msg.data的第一個字(word,也就是32個字節(jié))中的存儲長度會不一樣。如果我們想要只取出真正的數(shù)據(jù),我們需要跳過第一個字(word),從msg.data0x20(32個字節(jié))開始。然而,我們會用到兩個操作碼來實現(xiàn)此目的。我們會使用calldatasize來獲取msg.data的大小,以及calldatacopy來把它復(fù)制到ptr所指向的位置。

注意到我們是如何初始化ptr變量的。在solidity中,內(nèi)存槽中的0x40位置是和特殊的,因為它存儲了指向下一個可用自由內(nèi)存的指針。每次當(dāng)你想往內(nèi)存里存儲一個變量時,你都要檢查存儲在0x40的值。這就是你變量即將存放的位置。現(xiàn)在我們知道了我們要在哪兒存變量,我們就可以使用calldatacopy,把大小為calldatasize的calldata從0開始啊復(fù)制到ptr指向的那個位置了。

let ptr := mload(0x40)
calldatacopy(ptr, 0, calldatasize)

我們再看看下面的assembly代碼(使用delegatecall操作碼)

let result := delegatecall(gas, _impl,  ptr, calldatasize, 0, 0)

解釋一下上面的參數(shù):

gas:函數(shù)執(zhí)行所需的gas

_impl:我們調(diào)用的邏輯合約的地址

ptr:內(nèi)存指針(指向數(shù)據(jù)開始存儲的地方)

calldatasize:傳入的數(shù)據(jù)大小

0:調(diào)用邏輯合約后的返回值。我們沒有使用這個參數(shù)因為我們還不知道返回值的大小,所以不能把它賦值給一個變量。我們可以后面可以進一步使用returndata操作碼來獲取這些信息。

0:返回值的大小。這個參數(shù)也沒有被使用因為我們沒有機會創(chuàng)造一個臨時變量用來存儲返回值。鑒于我們在調(diào)用其他合約之前無法知道它的大小(所以就無法創(chuàng)造臨時變量呀)。我們稍后可以用returndatasize操作碼來得到這個值。

下面一行代碼就是用returndatasize操作碼得到了返回數(shù)據(jù)的大小:

let size := returndatasize

我們使用這個返回值的大小,來把返回值復(fù)制到ptr指向的內(nèi)存,使用returndatacopy來達到這個目的:

returndatacopy(ptr, 0, size)

最后,switch語句要么返回 【返回值】,要么拋出錯誤,如果發(fā)生錯誤的話。

很好,我們現(xiàn)在有了一個從邏輯合約中獲取正確結(jié)果的方法。

現(xiàn)在,我們理解了代理合約是如何工作的。下面就讓我們正式學(xué)習(xí)三種模式:繼承存儲模式、永久存儲模式和非結(jié)構(gòu)化存儲模式。

這三種方法用不同的方法來解決同一個難點:怎樣確保邏輯合約不會重寫/覆蓋代理中的狀態(tài)變量。

任何代理結(jié)構(gòu)模式的主要問題就是如何分配存儲。記住,既然我們使用一個合約來存儲,另一個合約來實現(xiàn)邏輯,它們中的任何一個都有可能重寫一個已經(jīng)使用的存儲槽。這意味著如果代理合約有一個狀態(tài)變量在某個存儲槽中存儲著最新的邏輯合約地址,但是邏輯合約卻不知道的話,那么邏輯合約可能就會在那個槽中存一些其他數(shù)據(jù),這樣就把代理合約中的重要信息覆蓋了。zeppelin的這三種方法代表了架構(gòu)合約系統(tǒng)的三種途徑,實現(xiàn)通過代理模式升級合約的目的。

使用繼承存儲模式升級

繼承存儲方法要求邏輯合約內(nèi)部也實現(xiàn)代理合約內(nèi)的存儲結(jié)構(gòu)。代理合約和邏輯合約都要繼承完全一樣的存儲結(jié)構(gòu),來確保二者都支持存儲必要的代理合約的狀態(tài)變量。

當(dāng)探索這種模式時,我們有這樣一個想法,我們想要有一個Registry合約來追蹤不同版本的邏輯合約。 為了升級成新的邏輯合約,你需要為它在Registry里注冊一個新的版本,并且要求代理合約中也升級成這個最新版本的邏輯合約。注意到有一個Registry合約并不影響存儲機制,實際上,它可以應(yīng)用到這篇文章中提到的任意一種存儲模式中。

如何初始化

部署Registry合約

部署一個邏輯合約的最初版本(V1),確保它繼承了Upgradeable合約

Registry合約中注冊這個最初版本(V1)的地址

要求Registry合約創(chuàng)建一個UpgradeabilityProxy實例

調(diào)用你的UpgrageabilityProxy實例來升級到你最初版本(V1)

如何升級

部署一個繼承了你最初版本合約的新版本(V2),==確保它保留了代理合約和最初版本邏輯合約中的存儲結(jié)構(gòu)==

Registry中注冊合約的新版本

調(diào)用你的UpgradeabilityProxy實例來升級到最新注冊的版本

tips

我們可以在未來部署的邏輯合約中升級現(xiàn)有方法、創(chuàng)造新的方法以及新的狀態(tài)變量,但仍然調(diào)用同一個UpgradeabilityProxy合約。

使用永久存儲模式升級

在永久存儲模式中,存儲模式用一個獨立的合約(代理和邏輯合約都要繼承這個合約)來定義。這個存儲合約保留了所有邏輯合約需要的狀態(tài)變量,因為代理合約也會知道這些變量的存在(因為繼承),它就可以為升級定義自己的狀態(tài)變量,不用考慮覆蓋變量這些問題。注意到所有的版本的邏輯合約都不可以再定義任何額外的狀態(tài)變量。所有版本的邏輯合約都必須一直使用一開始就定義好的永久存儲架構(gòu)。

這種應(yīng)用在zeppelin labs項目中提供了實現(xiàn),并且同時引入了代理所有權(quán)的概念。一個代理的所有者是唯一一個可以升級代理并指定一個新的邏輯合約的地址,也是唯一一個可以轉(zhuǎn)移所有權(quán)的地址。

如何初始化

部署一個EternalStorageProxy實例

部署一個邏輯合約的最初版本(V1)

調(diào)用EternalStorageProxy實例來升級到這個最初版本合約的地址

如果你的邏輯合約依賴自己的構(gòu)造函數(shù)(constructor)來設(shè)置某個初始狀態(tài),那么在它和代理合約產(chǎn)生聯(lián)系之后,之前的這些狀態(tài)就要重新修改,因為代理合約的存儲并不知道(邏輯合約里的)這些值。EternalStorageProxy有一個叫upgradeToAndCall的函數(shù)專門來調(diào)用一些邏輯合約中的方法,一旦代理合約升級到最新版本時,就把連接到的那個邏輯合約里的初始設(shè)置重新設(shè)置一遍。

如何升級

部署一個邏輯合約的最新版本(v2),確保它也包含永久存儲結(jié)構(gòu)。

調(diào)用EternalStorageProxy實例來升級到最新版本。

tips

這是沒有增加太多開銷同時很直觀的邏輯合約。 以后的邏輯合約可以升級現(xiàn)有的方法或者創(chuàng)造新的方法,但是不能引入新的狀態(tài)變量。

使用非結(jié)構(gòu)化存儲升級

非結(jié)構(gòu)化存儲模式和繼承存儲類似,但是不要求邏輯合約繼承任何和升級相關(guān)的狀態(tài)變量。這個模式使用代理合約中定義的非結(jié)構(gòu)化的存儲槽來保存升級所需的數(shù)據(jù)。

在代理合約中,我們定義了一個常量,每當(dāng)哈希的時候,就給出一個足夠隨機的存儲位置來存儲代理合約需要調(diào)用的邏輯合約的地址。

bytes32 private constant implementationPosition = 
                     keccak256("org.zeppelinos.proxy.implementation");

因為常量(恒定)狀態(tài)變量并不占用存儲槽,所以并不用擔(dān)心implementationPosition會不小心被邏輯合約占用。鑒于solidity在存儲中放置狀態(tài)變量的方法,依然有非常非常非常小的概率可能發(fā)生要存儲新變量的存儲槽已經(jīng)被占用了。

通過使用這種模式,任何版本的邏輯合約都不需要知道代理合約的存儲結(jié)構(gòu),但是所有后一個版本的邏輯合約都必須繼承上一個版本的存儲變量。就像在繼承存儲模式中一樣,未來的邏輯合約可以更新現(xiàn)有的方法,也可以創(chuàng)建新的方法和新的狀態(tài)變量。

這個模式也使用了代理合約所有權(quán)的概念。只有代理合約的所有者可以更新邏輯合約的地址,也是唯一可以轉(zhuǎn)移所有權(quán)的地址。

如何初始化

部署OwnedUpgradeabilityProxy實例

部署邏輯合約的初始版本(V1)

調(diào)用OwnedUpgradeabilityProxy實例來更新到初始版本的邏輯合約

如果你的邏輯合約依賴自己的構(gòu)造函數(shù)(constructor)來設(shè)置某個初始狀態(tài),那么在它和代理合約產(chǎn)生聯(lián)系之后,之前的這些狀態(tài)就要重新修改,因為代理合約的存儲并不知道(邏輯合約里的)這些值。OwnedUpgradeabilityProxy有一個upgradeToAndCall方法專門來調(diào)用一些邏輯合約中的方法,一旦代理合約升級到最新版本時,就把連接到的那個邏輯合約里的初始設(shè)置重新設(shè)置一遍。

如何升級

部署一個新版本的邏輯合約(V2),確保它繼承了上一個版本里的狀態(tài)變量結(jié)構(gòu)。

調(diào)用ownedUpgradeabilityProxy實例來升級到新版本合約的地址。

tips

這個方法很棒,因為它不需要邏輯合約知道它是整個代理系統(tǒng)的一部分。

關(guān)于升級

重要:如果你的邏輯合約依賴自己的構(gòu)造器來設(shè)置一些初始狀態(tài)的話,這個過程在新版本的邏輯合約注冊到代理中時需要重新做一遍。舉個例子,邏輯合約繼承Zeppelin中的Ownable合約,這很常見。當(dāng)你的邏輯合約繼承Ownable,它也就繼承了Ownable的構(gòu)造器,構(gòu)造器會在合約創(chuàng)建的時候就設(shè)置合約的所有者是誰。當(dāng)你讓代理合約來使用你的邏輯合約的時候,代理合約是不知道邏輯合約的所有者是誰的。

升級代理合約的一種常見的模式就代理立即對邏輯合約調(diào)用一個初始化方法。這個初始化方法應(yīng)該去模仿在構(gòu)造器中做的一些事情。同時你也想要一個標(biāo)識,用來確保你不可以再次對同一個邏輯合約調(diào)用初始化方法。(只能調(diào)用一次)

你的邏輯合約看上去可能像下面這樣:

contract Token is Ownable {
   ...
   bool internal _initialized;
   
   function initialize(address owner) public {
      require(!_initialized);
      setOwner(owner);
      _initialized = true;
   }
   ...
}

當(dāng)然這取決于你的部署策略,你可以寫一個幫助部署的合約,或者你可以可以多帶帶部署代理合約和邏輯合約。如果你多帶帶部署的話,你需要使用upgradeToAndCall把代理合約鏈接到邏輯合約上,這看上去就會像下面這樣:

const initializeData = encodeCall("initialize", ["address"], [tokenOwner])
await proxy.upgradeToAndCall(logicContract.address, initializeData, { from: proxyOwner })
結(jié)論

代理模式的概念已經(jīng)出來有一段時間了,但是由于太復(fù)雜了、害怕引入安全漏洞以及繞過了區(qū)塊鏈不可變的特性,它還沒有被廣泛接受。過去的解決方法在關(guān)于未來版本的邏輯合約可以添加和修改的東西上有嚴(yán)格的限制,這很不靈活。但是很顯然,開發(fā)者對于可升級合約的需求很迫切。zeppelin提供并且測試了三種模式,他們致力于幫助開發(fā)者架構(gòu)自己的項目,引入可升級特性。

盡管代理模式的概念出來也有段時間了,但是它的應(yīng)用依然處在非常早期。很開心看到越來越多的高級DApp架構(gòu)通過這種方式得以實現(xiàn)。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/24163.html

相關(guān)文章

  • 區(qū)塊鏈技術(shù)學(xué)習(xí)指引

    摘要:引言給迷失在如何學(xué)習(xí)區(qū)塊鏈技術(shù)的同學(xué)一個指引,區(qū)塊鏈技術(shù)是隨比特幣誕生,因此要搞明白區(qū)塊鏈技術(shù),應(yīng)該先了解下比特幣。但區(qū)塊鏈技術(shù)不單應(yīng)用于比特幣,還有非常多的現(xiàn)實應(yīng)用場景,想做區(qū)塊鏈應(yīng)用開發(fā),可進一步閱讀以太坊系列。 本文始發(fā)于深入淺出區(qū)塊鏈社區(qū), 原文:區(qū)塊鏈技術(shù)學(xué)習(xí)指引 原文已更新,請讀者前往原文閱讀 本章的文章越來越多,本文是一個索引帖,方便找到自己感興趣的文章,你也可以使用左側(cè)...

    Cristic 評論0 收藏0
  • SegmentFault 技術(shù)周刊 Vol.41 - 深入學(xué)習(xí)區(qū)塊鏈

    摘要:和比特幣協(xié)議有所不同的是,以太坊的設(shè)計十分靈活,極具適應(yīng)性。超級賬本區(qū)塊鏈的商業(yè)應(yīng)用超級賬本超級賬本是基金會下的眾多項目中的一個。證書頒發(fā)機構(gòu)負責(zé)簽發(fā)撤 showImg(https://segmentfault.com/img/bV2ge9?w=900&h=385); 從比特幣開始 一個故事告訴你比特幣的原理及運作機制 這篇文章的定位會比較科普,盡量用類比的方法將比特幣的基本原理講出來...

    qianfeng 評論0 收藏0
  • 智能合約開發(fā)環(huán)境搭建及Hello World合約

    摘要:今天我們來一步一步從搭建以太坊智能合約開發(fā)環(huán)境開始,講解智能合約的如何編寫。開發(fā)環(huán)境搭建安裝強烈建議新手使用來進行開發(fā)。第行修改部署賬戶為新賬戶索引,即使用新賬戶來部署合約。 本文首發(fā)于深入淺出區(qū)塊鏈社區(qū)原文鏈接:智能合約開發(fā)環(huán)境搭建及Hello World合約原文已更新,請讀者前往原文閱讀 如果你對于以太坊智能合約開發(fā)還沒有概念(本文會假設(shè)你已經(jīng)知道這些概念),建議先閱讀入門篇。就先...

    Winer 評論0 收藏0
  • 智能合約實施指南

    摘要:在協(xié)議結(jié)束時,智能合約被視為已履行并仍存儲在區(qū)塊鏈網(wǎng)絡(luò)中。這組條件和事件代表了最基本的一次性智能合約。智能合約用例智能合約越來越受歡迎,并已在各種區(qū)塊鏈項目中實施。 與區(qū)塊鏈技術(shù)一樣,智能合約在商業(yè)領(lǐng)域也非常有價值。 為了讓我們的讀者徹底了解智能合約是什么以及它們?nèi)绾斡绊懍F(xiàn)代商業(yè)的交易方式,我們準(zhǔn)備了本指南。 集中商業(yè)模式正在給去中心化的模式讓路 傳統(tǒng)的商業(yè)關(guān)系模型都是集中式的,始終存...

    meteor199 評論0 收藏0
  • 智能合約實施指南

    摘要:在協(xié)議結(jié)束時,智能合約被視為已履行并仍存儲在區(qū)塊鏈網(wǎng)絡(luò)中。這組條件和事件代表了最基本的一次性智能合約。智能合約用例智能合約越來越受歡迎,并已在各種區(qū)塊鏈項目中實施。 與區(qū)塊鏈技術(shù)一樣,智能合約在商業(yè)領(lǐng)域也非常有價值。 為了讓我們的讀者徹底了解智能合約是什么以及它們?nèi)绾斡绊懍F(xiàn)代商業(yè)的交易方式,我們準(zhǔn)備了本指南。 集中商業(yè)模式正在給去中心化的模式讓路 傳統(tǒng)的商業(yè)關(guān)系模型都是集中式的,始終存...

    PumpkinDylan 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<