成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專(zhuān)欄INFORMATION COLUMN

深度剖析智能合約升級(jí)——inherited storage

aervon / 2862人閱讀

摘要:接上篇合約升級(jí)模式介紹筆者改寫(xiě)了一個(gè)可用于實(shí)踐生產(chǎn)的升級(jí)框架,需要自取。在介紹合約升級(jí)模式中提到了一個(gè)可以解決這個(gè)問(wèn)題的方法。深度理解注意為中的低階方法下文中出現(xiàn)的方法,是我在智能合約中寫(xiě)的一個(gè)方法名稱(chēng),不要混淆。

接上篇:合約升級(jí)模式介紹

筆者改寫(xiě)了一個(gè)可用于實(shí)踐生產(chǎn)的升級(jí)框架,需要自取。https://github.com/hammewang/...

同時(shí)歡迎討論,微信xiuxiu1998

智能合約升級(jí)的目的

鑒于以太坊智能合約一旦部署,無(wú)法修改的原則,所以智能合約升級(jí)應(yīng)當(dāng)遵循如下兩點(diǎn)規(guī)則:

邏輯可升級(jí);

存儲(chǔ)可繼承;

第一點(diǎn)很好理解,可以把代理合約和邏輯合約看成插座和插頭的關(guān)系,需要升級(jí)的時(shí)候把老的插頭拔下,再插上新的即可。

對(duì)于第二點(diǎn),存儲(chǔ)可繼承,不僅僅是存儲(chǔ)結(jié)構(gòu)的繼承,而且在存儲(chǔ)內(nèi)容上,實(shí)現(xiàn)擴(kuò)展:舊存儲(chǔ)內(nèi)容不變,新存儲(chǔ)內(nèi)容繼續(xù)追加。這個(gè)過(guò)程類(lèi)似于城市化的推進(jìn),城市的邊緣可以一圈一圈擴(kuò)大,但是如果要尋址到老城區(qū)的XX路XX號(hào),無(wú)論城市怎么擴(kuò)大,拿著這個(gè)門(mén)牌號(hào)依然可以找到那棟老建筑。

升級(jí)方式

升級(jí)目的中的第一點(diǎn)是相對(duì)好實(shí)現(xiàn)的,只要改變調(diào)用的邏輯合約地址就可以了;而為了實(shí)現(xiàn)第二點(diǎn),就要保證合約執(zhí)行環(huán)境上下文保持一致。在介紹合約升級(jí)模式中提到了一個(gè)可以解決這個(gè)問(wèn)題的方法:delegatecall。把關(guān)鍵代碼再貼一遍:

 assembly {
    // 獲得自由內(nèi)存指針
    let ptr := mload(0x40)
    // 復(fù)制calldata到內(nèi)存中
    calldatacopy(ptr, 0, calldatasize) 
    // 使用delegatecall處理calldata
    let result := delegatecall(gas, _impl, ptr, calldatasize, 0, 0)
    // 返回值大小
    let size := returndatasize
    // 把返回值復(fù)制到內(nèi)存中
    returndatacopy(ptr, 0, size)

    switch result
    case 0 { revert(ptr, size) } // 執(zhí)行失敗
    default { return(ptr, size) } // 執(zhí)行成功,返回內(nèi)存中的返回值
 }

這樣做,實(shí)現(xiàn)了把邏輯合約(_impl)中的方法拉到代理合約中執(zhí)行,遵循代理合約的上下文(如存儲(chǔ)、余額等),通過(guò)這種方式實(shí)現(xiàn)了執(zhí)行上下文一致性。

深度理解delegatecall
注意:

delegatecall為assembly中的低階方法;

下文中出現(xiàn)的delegateCall方法,是我在智能合約中寫(xiě)的一個(gè)方法名稱(chēng),不要混淆。

delegatecall的目的是可以維持執(zhí)行環(huán)境中上下文的一致性,一種很典型的應(yīng)用場(chǎng)景就是調(diào)用library中的方法,用的就是delegatecall。下面來(lái)具體介紹一下delegatecall的特點(diǎn)。

1. 可以傳遞msg.sender

假設(shè)personA調(diào)用了contractA中的functionA,這個(gè)方法內(nèi)部同時(shí)使用了delegatecall調(diào)用了contractB中的functionB,那么對(duì)于functionB來(lái)說(shuō),msg.sender依然是personA,而不是contractA.

2.可以改變同一存儲(chǔ)槽中的內(nèi)容

請(qǐng)看下面的合約:

pragma solidity ^0.4.24;

contract proxy {
    address public logicAddress;
    
    function setLogic(address _a) public {
        logicAddress = _a;
    }
    
    function delegateCall(bytes data) public {
        this.call.value(msg.value)(data);
    }
    
    function () payable public {
    address _impl = logicAddress;
    require(_impl != address(0));

    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) }
    }
  }
    
    function getPositionAt(uint n) public view returns (address) {
        assembly {
            let d := sload(n)
            mstore(0x80, d)
            return(0x80,32)
      }
    }
}

contract logic {
    address public a;
     function setStorage(address _a) public {
         a = _a;
     }
}

這時(shí)分別部署proxylogic,之后把logic.address賦值給proxy中的logicAddress變量。調(diào)用getPositionAt(0)會(huì)發(fā)現(xiàn)返回的也是logicAddress的值,結(jié)果如下圖:

這時(shí),如果調(diào)用proxy中的delegateCall并傳入0x9137c1a7000000000000000000000000bcb9c87f53878af6dd7a8baf1b24bab6a62fe7aa9137c1a7setStorage的方法簽名),意為用delegatecall調(diào)用logic中的setStorage方法,這時(shí)會(huì)發(fā)現(xiàn)proxy中的logicAddress發(fā)生了變化,變成了我們剛剛傳入的值。如下:

這時(shí)我們會(huì)發(fā)現(xiàn),delegatecall并不通過(guò)變量名稱(chēng)來(lái)修改變量值,而是修改變量所在的存儲(chǔ)槽。所以當(dāng)在proxy中delegatecallsetStorage方法時(shí),修改的并不是address a,而是address a所在的第0個(gè)存儲(chǔ)槽的值,而proxy中第0個(gè)存儲(chǔ)槽存放的是logicAddress,所以相應(yīng)就會(huì)被覆蓋。

理解到這一步,就可以感受到delegatecall的強(qiáng)大和危險(xiǎn)。但同時(shí)也帶來(lái)一層疑問(wèn):雖然使用delegatecall可以使用邏輯合約中的方法改變代理合約中相應(yīng)位置的變量,但是并沒(méi)有起到存儲(chǔ)可擴(kuò)展呀?不還得事先在代理合約中創(chuàng)建相應(yīng)變量么?這就相當(dāng)于在1949年新中國(guó)建立的時(shí)候,就要規(guī)劃以后建設(shè)的所有布局,包括共享單車(chē)??奎c(diǎn),這不是有點(diǎn)扯淡么?

這就要說(shuō)到delegatecall下面一個(gè)特點(diǎn)了。

delegatecall——"無(wú)中生有"

delegatecall還有一個(gè)強(qiáng)大的特點(diǎn)就是,可以為proxy中未事先聲明的變量開(kāi)辟存儲(chǔ)空間。

我們來(lái)看下一個(gè)例子,代理合約依然使用上面用過(guò)的proxy,我們把邏輯合約 變一下:

contract logic2 {
    address public a;
    address public b;
     function setStorageB(address _a) public {
         b = _a;
     }
}

新增加一個(gè)address變量,并且只修改第二個(gè)address變量。

這時(shí)依然重復(fù)上一個(gè)例子的第一步,把logic2的地址賦值給代理合約中的logicAddress變量。結(jié)果如下圖:

然后使用代理合約中的detegateCall方法,調(diào)用logic2中的setStorage2方法,傳入data0x9ea338be0000000000000000000000000dcd2f752394c41875e259e00bb44fd505297caf。之后再調(diào)用getPositionAt(1)logicAddress()方法,結(jié)果如下圖:

可以看到logicAddress并沒(méi)有發(fā)生變化,而第1個(gè)存儲(chǔ)槽中的值變成了我們剛剛傳入的值。

這也再次說(shuō)明了,delegatecall方法并不是按照變量名稱(chēng)操作的,而是按照變量所對(duì)應(yīng)的存儲(chǔ)槽的位置,對(duì)該位置中的值進(jìn)行操作。因此,我們是不是事先在代理合約中聲明了變量,就并不重要了。

delegatecall總結(jié)

可以傳遞msg.sender

不按照變量名進(jìn)行操作,而是去找變量對(duì)應(yīng)的存儲(chǔ)槽進(jìn)行操作(無(wú)論變量是否在代理合約中事先聲明)

正因?yàn)榈诙c(diǎn)特性,為合約升級(jí)中的存儲(chǔ)擴(kuò)展提供了可能性;同時(shí),也提出了一個(gè)很?chē)?yán)格的要求:

新合約和舊合約之間必須嚴(yán)格遵守繼承的模式,即:

contract newLogic is previousVersionLogic{
    ...
}
使用存儲(chǔ)繼承模式升級(jí) 原理介紹
               -------             =========================
              | Proxy |           ║  UpgradeabilityStorage  ║
               -------             =========================
                  ↑                 ↑                     ↑            
                 ---------------------              -------------
                | UpgradeabilityProxy |            | Upgradeable |
                 ---------------------              ------------- 
                                                      ↑        ↑
                                              ----------      ---------- 
                                             | Token_V0 |  ← | Token_V1 |         
                                              ----------      ---------- 

代理合約是UpgradeabilityProxy實(shí)例,圖中的Token_V0Token_V1即是邏輯合約的最初版和升級(jí)版,它們都必須繼承Upgradeable,同時(shí)邏輯合約和代理合約都必須繼承UpgradeabilityStorage,繼承同一套存儲(chǔ)結(jié)構(gòu),以保證邏輯合約在代理合約中執(zhí)行時(shí),不會(huì)出現(xiàn)變量覆蓋的情況。

具體代碼結(jié)構(gòu)

注:圖中每個(gè)方框的結(jié)構(gòu)從上到下依次是:合約名稱(chēng)、狀態(tài)變量、function、event、modifier

圖中可以更加清晰地看到,代理合約和邏輯合約都必須繼承registry_implementation兩個(gè)狀態(tài)變量,并且邏輯合約中沒(méi)有修改前兩個(gè)狀態(tài)變量的相應(yīng)方法,因此代理合約中的存儲(chǔ)安全。

升級(jí)操作 1. 如何初始化

部署Registry合約

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

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

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

調(diào)用你的UpgrageabilityProxy實(shí)例來(lái)升級(jí)到你最初版本(V1)

2. 如何升級(jí)

部署一個(gè)繼承了你最初版本合約的新版本(V2),V2必須繼承V1

Registry中注冊(cè)合約的新版本V2

調(diào)用你的UpgradeabilityProxy實(shí)例來(lái)升級(jí)到最新注冊(cè)的版本

3. 如何轉(zhuǎn)移proxy合約所有權(quán)

調(diào)用Registry中的transferProxyOwnership方法進(jìn)行所有權(quán)轉(zhuǎn)移;

代碼調(diào)用注意事項(xiàng)

須對(duì)代理合約的地址套用當(dāng)前版本的邏輯合約的ABI,方能正常調(diào)用和獲取返回值。

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/24162.html

相關(guān)文章

  • 智能合約升級(jí)模式介紹 — 入門(mén)篇

    摘要:以后的邏輯合約可以升級(jí)現(xiàn)有的方法或者創(chuàng)造新的方法,但是不能引入新的狀態(tài)變量。使用非結(jié)構(gòu)化存儲(chǔ)升級(jí)非結(jié)構(gòu)化存儲(chǔ)模式和繼承存儲(chǔ)類(lèi)似,但是不要求邏輯合約繼承任何和升級(jí)相關(guān)的狀態(tài)變量。 以太坊最大的優(yōu)勢(shì)就是,每一筆用來(lái)轉(zhuǎn)賬、部署合約或者和合約交互的交易(事務(wù))都被存在一個(gè)叫做區(qū)塊鏈的公共賬本上。一旦交易發(fā)生,就再也無(wú)法隱藏或者改變。這帶來(lái)一個(gè)巨大的好處,就是在以太坊中的每一個(gè)節(jié)點(diǎn)都可以去驗(yàn)證任...

    YFan 評(píng)論0 收藏0
  • 區(qū)塊鏈技術(shù)學(xué)習(xí)指引

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

    Cristic 評(píng)論0 收藏0
  • 以太坊開(kāi)發(fā)實(shí)戰(zhàn)學(xué)習(xí)-solidity語(yǔ)法(二)

    摘要:以太坊開(kāi)發(fā)高級(jí)語(yǔ)言學(xué)習(xí)。地址以太坊區(qū)塊鏈由賬戶(hù)組成,你可以把它想象成銀行賬戶(hù)。使用很安全,因?yàn)樗哂幸蕴粎^(qū)塊鏈的安全保障除非竊取與以太坊地址相關(guān)聯(lián)的私鑰,否則是沒(méi)有辦法修改其他人的數(shù)據(jù)的。 以太坊開(kāi)發(fā)高級(jí)語(yǔ)言學(xué)習(xí)。 一、映射(Mapping)和地址(Address) 我們通過(guò)給數(shù)據(jù)庫(kù)中的僵尸指定主人, 來(lái)支持多玩家模式。 如此一來(lái),我們需要引入2個(gè)新的數(shù)據(jù)類(lèi)型:mapping(映射)...

    wemall 評(píng)論0 收藏0
  • 智能合約語(yǔ)言 Solidity 教程系列4 - 數(shù)據(jù)存儲(chǔ)位置分析

    摘要:狀態(tài)變量合約內(nèi)聲明的公有變量還有一個(gè)存儲(chǔ)位置是,用來(lái)存儲(chǔ)函數(shù)參數(shù),是只讀的,不會(huì)永久存儲(chǔ)的一個(gè)數(shù)據(jù)位置。稱(chēng)這個(gè)為狀態(tài)改變,這也是合約級(jí)變量稱(chēng)為狀態(tài)變量的原因。 本文首發(fā)于深入淺出區(qū)塊鏈社區(qū)原文鏈接:智能合約語(yǔ)言 Solidity 教程系列4 - 數(shù)據(jù)存儲(chǔ)位置分析原文已更新,請(qǐng)讀者前往原文閱讀 Solidity教程系列第4篇 - Solidity數(shù)據(jù)位置分析。 寫(xiě)在前面 Solidity...

    Galence 評(píng)論0 收藏0
  • 以太坊智能合約學(xué)習(xí)筆記(三)

    摘要:另外只能做狀態(tài)變量,不能做本地局部變量。語(yǔ)法聲明映射類(lèi)型包括類(lèi)型包括狀態(tài)變量報(bào)錯(cuò)??梢暥戎傅氖牵瑳Q定函數(shù)或者狀態(tài)變量的可以被哪些智能合約可見(jiàn)和調(diào)用。狀態(tài)變量可見(jiàn)性沒(méi)有。在中,通過(guò)來(lái)抽象出狀態(tài)變量自增的代碼,并修飾。 Mapping 映射(Mappings):類(lèi)似于哈希表。mapping 中任何一個(gè)可能的 key 都對(duì)應(yīng)著一個(gè) value,它的默認(rèn)值是default-value 。底層用...

    mumumu 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<