首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >智能契约-使用delegateCall中继更新实现

智能契约-使用delegateCall中继更新实现
EN

Ethereum用户
提问于 2017-06-14 22:35:07
回答 2查看 595关注 0票数 1

我知道智能契约应该是不可变的,这就是关键所在,但是期望有人从第一天起就实现一个永不改变的逻辑(没有升级或者没有bug)也是不现实的。

因此,我一直在阅读几种处理这种不变状态的方法。一种流行的方法似乎是使用带有中继合同的delegateCall,但是我很难实际使用这种方法,因为我找不到任何例子。

有人会对我创建的这个简单的例子表示感谢,并告诉我我在做什么不对吗?

https://gist.github.com/fabdarice/d513d620d9355312d085c7a68e6c6118

Relay.sol

代码语言:javascript
复制
contract Relay {
  address public currentVersion;
  address public owner;
  mapping (address => uint) user_amounts;


  modifier onlyOwner() {
    if (msg.sender != owner) {
        throw;
    }
    _;
  }

  function Relay(address initAddr) {
    currentVersion = initAddr;
    owner = msg.sender; // this owner may be another contract with multisig, not a single contract owner
  }

  function changeContract(address newVersion) public
  onlyOwner()
  {
    currentVersion = newVersion;
  }

  function() {
    if(!currentVersion.delegatecall(msg.data)) throw;
  }
}  

Donation.sol :

代码语言:javascript
复制
contract Donation {
  mapping (address => uint) user_amounts;    


  /* DOES THIS METHODS MODIFY user_amounts of the Relay contract ??? */
  function sendDonation(uint n) {
    user_amounts[msg.sender] = user_amounts[msg.sender] + n
  }
}  

DonationNew.sol :

代码语言:javascript
复制
contract DonationNew {
  mapping (address => uint) user_amounts;

  function sendDonation(uint n) {
    user_amounts[msg.sender] = user_amounts[msg.sender] + n
  }

  function cancelDonation() {
    user_amounts[msg.sender] = 0
  }
}  

app.js :

代码语言:javascript
复制
// First, deploying Relay, then deploying Donation and retrieve Donation contract address in 'donation_contract_address'

// Then, linking Relay to my first version of my contract Donation 
Relay.deployed().then(function(contractInstance) {
   contractInstance.changeContract(donation_contract_address);
})

// Then, I want to call sendDonation from the Donation contract
// !!!!! I DON'T KNOW WHAT IS THE CORRECT WAY TO CALL THIS !!!!!!
Relay.deployed().then(function(contractInstance) {
   contractInstance.sendDonation(5) ;
})
// OR 
Relay.deployed().then(function(contractInstance) {
   contractInstance.currentVersion.delegateCall(sendDonation(5)) ;
})

// Now I want to update the Donation contract to add the cancelDonation function
// First I deploy the new contract DonationNew and retrieve it's address in 'donation_new_contract_address'
Relay.deployed().then(function(contractInstance) {
   contractInstance.changeContract(donation_new_contract_address);
})

// are the state variables still available from the old contract to the new one?

// Then if I want to call the new function : 
Relay.deployed().then(function(contractInstance) {
   contractInstance.cancelDonation() ;
})

P.S.:我知道上面的方法允许我们在需要更新逻辑(函数等)的情况下创建一个“可升级的契约”,但是它不允许我们修改/添加状态变量结构。有没有办法解决这个问题?

非常感谢,很高兴成为这个社区的一员!

EN

回答 2

Ethereum用户

发布于 2017-11-06 09:44:33

这只是对您的P.S.的一个回答,因为我还没有很好地了解您的代码到底出了什么问题。

如果您希望能够在保持存储的同时升级代码,则可以考虑将存储和逻辑分离。有一个专用的存储契约,它接受来自受信任地址(例如逻辑契约)的写调用。所有重要的存储都应该与此one.You相关联,因此也应该考虑使其尽可能灵活,这样就不需要进行升级了。

本文包含一个示例以及编写可升级智能合同的许多其他建议:

https://blog.colony.io/writing-upgradeable-contracts-in-solidity-6743f0eecc88

票数 0
EN

Ethereum用户

发布于 2018-08-27 22:20:44

下面是一个可升级库的示例。

https://github.com/kyriediculous/knuckles/tree/master/contracts/contracts

我目前正在努力把它砍掉。

有一个中央注册表来跟踪正在使用的库地址。更改这些更改将更改代理委托到的地址。

代理通过库的接口链接到存储。这将构造调用数据,将其发送给代理,然后代理将其委托给库。

如果要升级库,请重新部署它,并更改中央注册表中的地址。

下面是一个简短的例子,博客帖子很快就来了。

代码语言:javascript
复制
pragma solidity ^0.4.23;

contract Registry {
  mapping (bytes32 => address) public libraries;
  mapping (bytes32 => address) public contracts;

    function addLibrary(bytes32 _name, address _lib) external {
    require(libraries[_name] == address(0), "LIBRARY_ALREADY_EXISTS");
    require(_lib != address(0), "INSERT_VALID_LIBRARY_ADDRESS");
    libraries[_name] = _lib;
  }

  function addContract(bytes32 _name, address _contract) external {
    Enabled(_contract).setCMCAddress(address(this));
    contracts[_name] = _contract;
  }
}

interface ContractProvider {
    function libraries(bytes32 _name) external view returns (address);
    function contracts(bytes32 _name) external view returns (address);
}

contract Enabled {
  address public CMC;
  function setCMCAddress(address _CMC) external {
    if (CMC != 0x0 && msg.sender != CMC) {
        revert();
    } else {
        CMC = _CMC;
    }
  }
}

contract setXproxy is Enabled {
  function () payable public {
    address _impl =  ContractProvider(CMC).libraries('setXlib');
    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) }
    }
  }
}

library setXinterface {
    struct X {
        uint x;
    }

    function setX(X storage _X, uint _x) external;
}

library setXlib {
    function setX(setXinterface.X storage _X, uint _x) external {
        _X.x = _x;
    }
}

contract setXstorage is Enabled {
    using setXinterface for setXinterface.X;
    setXinterface.X X;
    function setX(uint _x) external {
        X.setX(_x);
    }

    function getX() external view returns (uint) {
      return X.x;
    }
}
票数 0
EN
页面原文内容由Ethereum提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://ethereum.stackexchange.com/questions/18003

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档