我知道智能契约应该是不可变的,这就是关键所在,但是期望有人从第一天起就实现一个永不改变的逻辑(没有升级或者没有bug)也是不现实的。
因此,我一直在阅读几种处理这种不变状态的方法。一种流行的方法似乎是使用带有中继合同的delegateCall,但是我很难实际使用这种方法,因为我找不到任何例子。
有人会对我创建的这个简单的例子表示感谢,并告诉我我在做什么不对吗?
https://gist.github.com/fabdarice/d513d620d9355312d085c7a68e6c6118
Relay.sol
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;
}
} 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
}
} 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
}
} // 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.:我知道上面的方法允许我们在需要更新逻辑(函数等)的情况下创建一个“可升级的契约”,但是它不允许我们修改/添加状态变量结构。有没有办法解决这个问题?
非常感谢,很高兴成为这个社区的一员!
发布于 2017-11-06 09:44:33
这只是对您的P.S.的一个回答,因为我还没有很好地了解您的代码到底出了什么问题。
如果您希望能够在保持存储的同时升级代码,则可以考虑将存储和逻辑分离。有一个专用的存储契约,它接受来自受信任地址(例如逻辑契约)的写调用。所有重要的存储都应该与此one.You相关联,因此也应该考虑使其尽可能灵活,这样就不需要进行升级了。
本文包含一个示例以及编写可升级智能合同的许多其他建议:
https://blog.colony.io/writing-upgradeable-contracts-in-solidity-6743f0eecc88
发布于 2018-08-27 22:20:44
下面是一个可升级库的示例。
https://github.com/kyriediculous/knuckles/tree/master/contracts/contracts
我目前正在努力把它砍掉。
有一个中央注册表来跟踪正在使用的库地址。更改这些更改将更改代理委托到的地址。
代理通过库的接口链接到存储。这将构造调用数据,将其发送给代理,然后代理将其委托给库。
如果要升级库,请重新部署它,并更改中央注册表中的地址。
下面是一个简短的例子,博客帖子很快就来了。
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;
}
}https://ethereum.stackexchange.com/questions/18003
复制相似问题