麻豆国内精品欧美在线-麻豆国内精品久久久久久-麻豆国产在线观看一区二区-麻豆国产在线观看免费-麻豆国产原创-麻豆国产一区二区在线观看

如何實現(xiàn)可升級的智能合約?區(qū)塊鏈

hackernoon 2018-09-18 14:52
分享到:
導(dǎo)讀

一般來說,開發(fā)人員可以很容易地升級他們的軟件,但區(qū)塊鏈的情況是不一樣滴,因為它們有著難以更改的屬性。

智能合約的重要性已越來越明顯,現(xiàn)如今,整個密碼貨幣生態(tài)系統(tǒng)都是由智能合約所驅(qū)動!不管我們有多小心,或者我們的代碼測試工作做得有多好,如果我們創(chuàng)建的是一個復(fù)雜的系統(tǒng),那我們就有必要更新合約邏輯,以修補(bǔ)其存在的漏洞,或者添加必要的缺失功能。有時候,由于EVM虛擬機(jī)的更改或者被新發(fā)現(xiàn)的漏洞,我們可能需要去升級我們的智能合約。

一般來說,開發(fā)人員可以很容易地升級他們的軟件,但區(qū)塊鏈的情況是不一樣滴,因為它們有著難以更改的屬性。如果我們部署了一個合約,這就好比是潑出去的水。然而,如果我們使用適當(dāng)?shù)募夹g(shù),我們可以在不同的地址部署一個新的合約,并使得舊合約無效。下面是一些最常見的,創(chuàng)建可升級智能合約的方法。

rt

主從合約(Master-Slave contract)

主從技術(shù),是可實現(xiàn)升級智能合約最為基礎(chǔ)也是最容易理解的技術(shù)之一。在這種技術(shù)當(dāng)中,我們部署一個主合約,以及其他合約,其中主合約負(fù)責(zé)存儲所有其他合約的地址,并在需要時返回所需的地址。當(dāng)這些合約需要和其它合約進(jìn)行溝通時,它們會充當(dāng)從合約,從主合約那里獲取其它合約的最新地址。為了升級智能合約,我們只需要在網(wǎng)絡(luò)上部署它,并更改主合約中的地址。雖然這遠(yuǎn)不是發(fā)展可升級智能合約的最佳方式,但它確是最簡單的。這種方法存在著很多的局限性,其中之一是,我們不能輕易地把合約的數(shù)據(jù)或資產(chǎn)遷移到新合約中。

永久存儲合約(Eternal Storage contract)

在這種技術(shù)當(dāng)中,我們將邏輯合約和數(shù)據(jù)合約彼此分離。數(shù)據(jù)合約應(yīng)該是永久并且不可升級的。而邏輯合約可以根據(jù)需要進(jìn)行多次升級,并將變化通知給數(shù)據(jù)合約。這是一項相當(dāng)基本的技術(shù),并且存在著一個明顯的缺陷。由于數(shù)據(jù)合約是不可升級的,數(shù)據(jù)結(jié)構(gòu)中需要的任何更改,或數(shù)據(jù)合約中存在的漏洞,都會導(dǎo)致所有數(shù)據(jù)變得無用。這種技術(shù)的另一個問題是,如果邏輯合約想要訪問/操作區(qū)塊鏈上的數(shù)據(jù),那么這個邏輯合約將需要進(jìn)行外部調(diào)用,而外部調(diào)用會消耗額外的gas。通常情況下,這種技術(shù)會和主從技術(shù)相結(jié)合,以促進(jìn)合約間的通信。

可升級存儲代理合約

我們可通過使永久存儲合約充當(dāng)邏輯合約的代理,以此防止支付額外的gas。這個代理合約,以及這個邏輯合約,將繼承同一存儲合約,那么它們的存儲會在EVM虛擬機(jī)中對齊。這個代理合約將有一個回退函數(shù),它將委托調(diào)用這個邏輯合約,那么這個邏輯合約就可以在代理存儲中進(jìn)行更改。這個代理合約將是永恒的。這節(jié)省了對存儲合約多次調(diào)用所需的gas,不管數(shù)據(jù)做了多少的更改,就只需要一次委托調(diào)用。

這項技術(shù)當(dāng)中有三個組成部分:

代理合約(Proxy contract):它將充當(dāng)永久存儲并負(fù)責(zé)委托調(diào)用邏輯合約;

邏輯合約(Logic contract):它負(fù)責(zé)完成處理所有的數(shù)據(jù);

存儲結(jié)構(gòu)(Storage structure):它包含了存儲結(jié)構(gòu),并會由代理合約和邏輯合約所繼承,以便它們的存儲指針能夠在區(qū)塊鏈上保持同步;

p1

委托調(diào)用

該技術(shù)的核心在于EVM所提供的DELEGATECALL操作碼,DELEGATECALL就像是一個普通的CALL 調(diào)用操作碼,不同之處在于目標(biāo)地址上的代碼是在調(diào)用合約上下文中執(zhí)行的,而原始調(diào)用的msg.sender以及msg.value將被保留。簡單說,DELEGATECALL基本上允許(委托)目標(biāo)合約在調(diào)用合約的存儲中做它任何想做的事情。

我們將利用這一點,并創(chuàng)建一個代理合約,它將使用DELEGATECALL操作碼委托調(diào)用邏輯合約,這樣我們就可以在代理合約中保持?jǐn)?shù)據(jù)的安全,同時我們可以自由地更改邏輯合約。

如何使用可升級存儲代理合約?

讓我們深入研究一下細(xì)節(jié)。我們需要的第一個合約是存儲結(jié)構(gòu)。它將定義我們需要的所有存儲變量,并將由代理合約和執(zhí)行合約所繼承。它看起來會是這樣的:

contract StorageStructure {
address public implementation;
address public owner;
mapping (address => uint) internal points;
uint internal totalPlayers;
}

我們現(xiàn)在需要一個執(zhí)行/邏輯合約。讓我們創(chuàng)建一個簡單版的合約,在添加新玩家時不會增加totalPlayers計數(shù)器的數(shù)字。

contract ImplementationV1 is StorageStructure {
modifier onlyOwner() {
require (msg.sender == owner);
_;
}
function addPlayer(address _player, uint _points)
public onlyOwner
{
require (points[_player] == 0);
points[_player] = _points;
}
function setPoints(address _player, uint _points)
public onlyOwner
{
require (points[_player] != 0);
points[_player] = _points;
}
}

下面就是最關(guān)鍵的部分:代理合約;

contract Proxy is StorageStructure {modifier onlyOwner() {
require (msg.sender == owner);
_;
}/**
* @dev constructor that sets the owner address
*/
constructor() public {
owner = msg.sender;
}/**
* @dev Upgrades the implementation address
* @param _newImplementation address of the new implementation
*/
function upgradeTo(address _newImplementation)
external onlyOwner
{
require(implementation != _newImplementation);
_setImplementation(_newImplementation);
}/**
* @dev Fallback function allowing to perform a delegatecall
* to the given implementation. This function will return
* whatever the implementation call returns
*/
function () payable public {
address impl = implementation;
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) }
}
}/**
* @dev Sets the address of the current implementation
* @param _newImp address of the new implementation
*/
function _setImplementation(address _newImp) internal {
implementation = _newImp;
}
}

為了讓合約生效,我們首先需要部署代理合約以及ImplementationV1合約,然后調(diào)用這個代理合約的upgradeTo(address)函數(shù),同時pass掉我們的ImplementationV1合約地址。現(xiàn)在,我們可以忘記這個ImplementationV1合約的地址,并把代理合約的地址作為我們的主地址。

為了升級這個合約,我們需要創(chuàng)建一個新的邏輯合約實現(xiàn),它可以是這樣的:

contract ImplementationV2 is ImplementationV1 {function addPlayer(address _player, uint _points)
public onlyOwner
{
require (points[_player] == 0);
points[_player] = _points;
totalPlayers  ;
}
}

你應(yīng)該注意到,這個合約也繼承了存儲結(jié)構(gòu)合約(StorageStructure contract),盡管它是間接地。

所有的執(zhí)行方案都必須繼承這個存儲結(jié)構(gòu)合約,并且在部署代理合約后不得進(jìn)行更改,以避免對代理的存儲進(jìn)行意外覆蓋。

為了實現(xiàn)升級,我們在網(wǎng)絡(luò)上部署這個合約,然后調(diào)用代理合約的upgradeTo(address) 函數(shù),同時pass掉ImplementationV2合約的地址。

這種技術(shù),使得升級合約邏輯變得相當(dāng)容易,但它仍然不允許我們升級合約的存儲結(jié)構(gòu)。我們可以通過使用非結(jié)構(gòu)化的代理合約來解決這個問題。

非結(jié)構(gòu)化可升級存儲代理合約

這是當(dāng)前最先進(jìn)的,可實現(xiàn)智能合約升級的方法之一。它通過保存合約地址以及在存儲中固定位置所有者的方法,以實現(xiàn)它們不會被執(zhí)行/邏輯合約提供的數(shù)據(jù)所覆蓋。我們可以使用sload以及sstore操作碼來直接讀取和寫入由固定指針?biāo)玫奶囟ù鎯Σ邸?/p>

此方法利用了存儲中狀態(tài)變量的布局,以避免邏輯合約覆蓋掉固定位置。如果我們將固定位置設(shè)置為0x7,那么在使用前7個存儲槽后,它就會被覆蓋掉。為了避免這種情況,我們將固定位置設(shè)置為類似keccak256(“org.govblocks.implemenation.address”).

這消除了在代理合約中繼承存儲結(jié)構(gòu)合約的需要,這意味著我們現(xiàn)在也可以升級存儲結(jié)構(gòu)了。然而,升級存儲結(jié)構(gòu)是一項棘手的任務(wù),因為我們需要確保,我們所提交的更改,不會導(dǎo)致新的存儲布局與先前的存儲布局不匹配。

這項技術(shù)有兩個組成部分。

1、代理合約:它負(fù)責(zé)將執(zhí)行合約的地址存儲在一個固定的地址當(dāng)中,并負(fù)責(zé)委托調(diào)用它; 2、執(zhí)行合約:它是主要合約,負(fù)責(zé)把我邏輯以及存儲結(jié)構(gòu);

你甚至可以將這項技術(shù)用于你現(xiàn)有的合約,因為它不需要對你的執(zhí)行合約進(jìn)行任何更改。

這個代理合約會是這樣子的:

contract UnstructuredProxy {// Storage position of the address of the current implementation
bytes32 private constant implementationPosition =
keccak256("org.govblocks.implementation.address");// Storage position of the owner of the contract
bytes32 private constant proxyOwnerPosition =
keccak256("org.govblocks.proxy.owner");/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyProxyOwner() {
require (msg.sender == proxyOwner());
_;
}/**
* @dev the constructor sets owner
*/
constructor() public {
_setUpgradeabilityOwner(msg.sender);
}/**
* @dev Allows the current owner to transfer ownership
* @param _newOwner The address to transfer ownership to
*/
function transferProxyOwnership(address _newOwner)
public onlyProxyOwner
{
require(_newOwner != address(0));
_setUpgradeabilityOwner(_newOwner);
}/**
* @dev Allows the proxy owner to upgrade the implementation
* @param _implementation address of the new implementation
*/
function upgradeTo(address _implementation)
public onlyProxyOwner
{
_upgradeTo(_implementation);
}/**
* @dev Tells the address of the current implementation
* @return address of the current implementation
*/
function implementation() public view returns (address impl) {
bytes32 position = implementationPosition;
assembly {
impl := sload(position)
}
}/**
* @dev Tells the address of the owner
* @return the address of the owner
*/
function proxyOwner() public view returns (address owner) {
bytes32 position = proxyOwnerPosition;
assembly {
owner := sload(position)
}
}/**
* @dev Sets the address of the current implementation
* @param _newImplementation address of the new implementation
*/
function _setImplementation(address _newImplementation)
internal
{
bytes32 position = implementationPosition;
assembly {
sstore(position, _newImplementation)
}
}/**
* @dev Upgrades the implementation address
* @param _newImplementation address of the new implementation
*/
function _upgradeTo(address _newImplementation) internal {
address currentImplementation = implementation();
require(currentImplementation != _newImplementation);
_setImplementation(_newImplementation);
}/**
* @dev Sets the address of the owner
*/
function _setUpgradeabilityOwner(address _newProxyOwner)
internal
{
bytes32 position = proxyOwnerPosition;
assembly {
sstore(position, _newProxyOwner)
}
}
}

如何使用非結(jié)構(gòu)化可升級存儲代理合約?

使用非結(jié)構(gòu)化可升級存儲代理合約是非常簡單的,因為這種技術(shù)幾乎可以處理所有現(xiàn)有的合約。想要使用這種技術(shù),你只需要遵循以下步驟:

部署代理合約和執(zhí)行合約;

調(diào)用代理合約的upgradeTo(address)函數(shù),同時pass掉執(zhí)行合約的地址。

我們現(xiàn)在可以忘掉這個執(zhí)行合約地址,然后把代理合約的地址作為主地址。

而要升級這個新實施的合約,我們只需要部署新的執(zhí)行合約,并調(diào)用代理合約的upgradeTo(address) 函數(shù),同時pass掉這個新執(zhí)行合約的地址。就是這么簡單!

讓我們簡單舉個例子。我們將再次使用上述可升級存儲代理合約中使用的同一邏輯合約,但是我們不需要用到存儲結(jié)構(gòu)。因此,我們的ImplementationV1合約看起來會是這樣的:

contract ImplementationV1 {
address public owner;
mapping (address => uint) internal points;modifier onlyOwner() {
require (msg.sender == owner);
_;
}function initOwner() external {
require (owner == address(0));
owner = msg.sender;
}function addPlayer(address _player, uint _points)
public onlyOwner
{
require (points[_player] == 0);
points[_player] = _points;
}function setPoints(address _player, uint _points)
public onlyOwner
{
require (points[_player] != 0);
points[_player] = _points;
}
}

下一步是部署這個執(zhí)行合約以及我們的代理合約。然后,再調(diào)用代理合約的upgradeTo(address) 函數(shù),同時pass掉執(zhí)行合約的地址。

你可能注意到,在這個執(zhí)行合約中,甚至沒有聲明totalPlayers變量,我們可以升級這個執(zhí)行合約,其中具有 totalPlayers變量,這個新的執(zhí)行合約看起來會是這樣的:

contract ImplementationV2 is ImplementationV1 {
uint public totalPlayers;function addPlayer(address _player, uint _points)
public onlyOwner
{
require (points[_player] == 0);
points[_player] = _points;
totalPlayers  ;
}
}

而要升級這個新的執(zhí)行合約,我們需要做的,就是在網(wǎng)絡(luò)上部署這個合約,然后,嗯你猜對了,就是調(diào)用代理合約的upgradeTo(address)函數(shù),并同時pass掉我們新執(zhí)行合約的地址。現(xiàn)在,我們的合約已演變?yōu)槟軌虮3指?totalPlayers,同時仍然為用戶提供相同的地址。

這種方法是強(qiáng)大的,但也存在著一些局限性。主要關(guān)注的一點是,代理合約擁有者(proxyOwner)有太多的權(quán)力。而且,這種方法對復(fù)雜的系統(tǒng)而言是不夠的。對于構(gòu)建具有可升級合約的 dApp而言,組合主從合約以及非結(jié)構(gòu)化可升級存儲代理合約,會是更為靈活的一種方法,這也是作者所在的GovBlocks所使用的方法。

結(jié)論

非結(jié)構(gòu)化存儲代理合約,是創(chuàng)建可升級智能合約最先進(jìn)的技術(shù)之一,但它仍然是不完美的。畢竟,我們并不希望dApp所有者對dApp具有不當(dāng)?shù)目刂茩?quán)。如果開發(fā)者擁有了這種權(quán)力,那這個dapp還能稱之為去中心化應(yīng)用嗎?在這里,我建議讀者可以閱讀下Nitika提出的反對使用onlyOwner的論點。你也可以在GitHub上窺探到我們的代理合約。

希望這篇文章可以幫助你創(chuàng)建可升級的智能合約。

同時向Zepplin在代理技術(shù)方面進(jìn)行的工作致敬。

合約 代理 存儲 升級 address
分享到:

1.TMT觀察網(wǎng)遵循行業(yè)規(guī)范,任何轉(zhuǎn)載的稿件都會明確標(biāo)注作者和來源;
2.TMT觀察網(wǎng)的原創(chuàng)文章,請轉(zhuǎn)載時務(wù)必注明文章作者和"來源:TMT觀察網(wǎng)",不尊重原創(chuàng)的行為TMT觀察網(wǎng)或?qū)⒆肪控?zé)任;
3.作者投稿可能會經(jīng)TMT觀察網(wǎng)編輯修改或補(bǔ)充。


主站蜘蛛池模板: 国产精品美女久久久久 | 91久| caoporm碰最新免费公开视频 | 日韩成片 | 国产在线观看福利片 | 丝袜高跟小说 | 久久免费看少妇高潮A片特爽 | 男生和老师一起差差差 | 午夜dj影院在线观看完整版 | 大妹子最新视频在线观看 | 爽好舒服宝贝添奶吻戏 | 欧美一卡2卡3卡四卡海外精品 | 成年人视频免费在线播放 | 热99re久久精品国产 | 午夜私人影院在线观看 | www.四虎影 | 车上小婕子系列辣文小说 | 国偷盗摄自产福利一区在线 | 卫生间被教官做好爽HH视频 | 成人国产午夜在线视频 | 女人张开腿让男人桶视频免费大全 | 污樱桃视频 | 99精品在线视频 | 韩国三级hd中文字幕李采潭 | 国产精品久久久久久久牛牛 | 欧美日韩中文字幕久久伊人 | 国产良心大作白丝精厕 | 精品一区二区三区视频日产 | 亚洲精品一区制服丝袜 | 久久99视热频国只有精品 | 亚洲天堂男人的天堂 | 亚洲国产成人综合 | 国产精品第四页 | 国产日韩欧美在线一区二区三区 | 四虎国产精品免费久久麻豆 | 国产香蕉在线视频 | 国产在视频线精品视频 | 亚洲天堂v | 成人精品区 | 国产玖玖在线 | 91精品国产在线 |