From 2a0495f9f748baaadaedd4240c9523f7ea827239 Mon Sep 17 00:00:00 2001 From: Ricardo Guilherme Schmidt Date: Thu, 14 Feb 2019 02:18:08 -0200 Subject: [PATCH] update instance factory --- contracts/deploy/DelegatedCall.sol | 44 ++++++------- contracts/deploy/Factory.sol | 90 -------------------------- contracts/deploy/Instance.sol | 49 +++++++------- contracts/deploy/InstanceAbstract.sol | 15 +++++ contracts/deploy/InstanceFactory.sol | 75 +++++++++++++++++++++ contracts/deploy/InstanceStorage.sol | 16 ----- contracts/deploy/PrototypeRegistry.sol | 86 ++++++++++++++++++++++++ contracts/deploy/UpdatableInstance.sol | 28 -------- 8 files changed, 223 insertions(+), 180 deletions(-) delete mode 100644 contracts/deploy/Factory.sol create mode 100644 contracts/deploy/InstanceAbstract.sol create mode 100644 contracts/deploy/InstanceFactory.sol delete mode 100644 contracts/deploy/InstanceStorage.sol create mode 100644 contracts/deploy/PrototypeRegistry.sol delete mode 100644 contracts/deploy/UpdatableInstance.sol diff --git a/contracts/deploy/DelegatedCall.sol b/contracts/deploy/DelegatedCall.sol index 8612147..b623ac9 100644 --- a/contracts/deploy/DelegatedCall.sol +++ b/contracts/deploy/DelegatedCall.sol @@ -1,39 +1,37 @@ -pragma solidity ^0.4.23; +pragma solidity >=0.5.0 <0.6.0; /** * @title DelegatedCall * @author Ricardo Guilherme Schmidt (Status Research & Development GmbH) - * @dev Abstract contract that delegates calls by `delegated` modifier to result of `targetDelegatedCall()` - * Important to avoid overwriting wrong storage pointers is that never define storage to this contract + * @dev Encapsulates delegatecall related logic. */ contract DelegatedCall { - constructor() internal { - + constructor(address _init, bytes memory _initMsg) internal { + if(_init == address(0)) return; + bool success; + (success, ) = _init.delegatecall(_initMsg); + require(success, "Delegated Construct fail"); } /** * @dev delegates the call of this function */ - modifier delegated { - //require successfull delegate call to remote `_target()` - require(targetDelegatedCall().delegatecall(msg.data)); - assembly { - let outSize := returndatasize - let outDataPtr := mload(0x40) //load memory - returndatacopy(outDataPtr, 0, outSize) //copy last return into pointer - return(outDataPtr, outSize) + modifier delegateAndReturn(address _target) { + if(_target == address(0)) { + _; //normal execution + } else { + //delegated execution + bytes memory returnData; + bool success; + (success, returnData) = _target.delegatecall(msg.data); + require(success, "Delegated Call failed"); + + //exit-return delegatecall returnData + assembly { + return(add(returnData, 0x20), returnData) + } } - assert(false); //should never reach here - _; //never will execute local logic } - /** - * @dev defines the address for delegation of calls - */ - function targetDelegatedCall() - internal - view - returns(address); - } diff --git a/contracts/deploy/Factory.sol b/contracts/deploy/Factory.sol deleted file mode 100644 index 52c0707..0000000 --- a/contracts/deploy/Factory.sol +++ /dev/null @@ -1,90 +0,0 @@ -pragma solidity ^0.4.23; - -import "../common/Controlled.sol"; - -contract Factory is Controlled { - - event NewKernel(address newKernel, bytes32 codeHash); - - struct Version { - uint256 blockNumber; - uint256 timestamp; - address kernel; - bytes32 codeHash; - } - - mapping (address => uint256) versionMap; - - Version[] versionLog; - uint256 latestUpdate; - address latestKernel; - - constructor(address _kernel) - public - { - _setKernel(_kernel); - } - - function setKernel(address _kernel) - external - onlyController - { - _setKernel(_kernel); - } - - function getVersion(uint256 index) - public - view - returns( - uint256 blockNumber, - uint256 timestamp, - address kernel, - bytes32 codeHash - ) - { - return ( - versionLog[index].blockNumber, - versionLog[index].timestamp, - versionLog[index].kernel, - versionLog[index].codeHash - ); - } - - function getCodeHash(address _addr) - public - view - returns (bytes32 codeHash) - { - bytes memory o_code; - uint size; - assembly { - // retrieve the size of the code, this needs assembly - size := extcodesize(_addr) - } - require (size > 0); - assembly { - // allocate output byte array - this could also be done without assembly - // by using o_code = new bytes(size) - o_code := mload(0x40) - // new "memory end" including padding - mstore(0x40, add(o_code, and(add(add(size, 0x20), 0x1f), not(0x1f)))) - // store length in memory - mstore(o_code, size) - // actually retrieve the code, this needs assembly - extcodecopy(_addr, add(o_code, 0x20), 0, size) - } - codeHash = keccak256(o_code); - } - - function _setKernel(address _kernel) - internal - { - require(_kernel != latestKernel); - bytes32 _codeHash = getCodeHash(_kernel); - versionMap[_kernel] = versionLog.length; - versionLog.push(Version({blockNumber: block.number, timestamp: block.timestamp, kernel: _kernel, codeHash: _codeHash})); - latestUpdate = block.timestamp; - latestKernel = _kernel; - emit NewKernel(_kernel, _codeHash); - } -} \ No newline at end of file diff --git a/contracts/deploy/Instance.sol b/contracts/deploy/Instance.sol index 967bb5f..8bddb5c 100644 --- a/contracts/deploy/Instance.sol +++ b/contracts/deploy/Instance.sol @@ -1,38 +1,41 @@ -pragma solidity ^0.4.21; +pragma solidity >=0.5.0 <0.6.0; -import "./InstanceStorage.sol"; +import "./InstanceAbstract.sol"; import "./DelegatedCall.sol"; /** * @title Instance * @author Ricardo Guilherme Schmidt (Status Research & Development GmbH) - * @dev Contract that forward everything through delegatecall to defined kernel + * @dev Contract that forward everything through delegatecall to defined base */ -contract Instance is InstanceStorage, DelegatedCall { +contract Instance is InstanceAbstract, DelegatedCall { - constructor(address _kernel) public { - kernel = _kernel; + /** + * @notice delegatecall `_init` with `_initMsg` and set base as `_base` + * @param _base base for delegatecall + * @param _init constructor contract + * @param _initMsg arguments to be passed for the single delegatecall on `_init` + */ + constructor( + InstanceAbstract _base, + InstanceAbstract _init, + bytes memory _initMsg + ) + public + payable + DelegatedCall(address(_init), _initMsg) + { + base = _base; } /** * @dev delegatecall everything (but declared functions) to `_target()` - * @notice Verify `kernel()` code to predict behavior + * @notice Verify `base()` code to predict behavior */ - function () external delegated { - //all goes to kernel - } - - /** - * @dev returns kernel if kernel that is configured - * @return kernel address - */ - function targetDelegatedCall() - internal - view - returns(address) - { - return kernel; - } - + function () + external + payable + delegateAndReturn(address(base)) + { } } \ No newline at end of file diff --git a/contracts/deploy/InstanceAbstract.sol b/contracts/deploy/InstanceAbstract.sol new file mode 100644 index 0000000..11d388b --- /dev/null +++ b/contracts/deploy/InstanceAbstract.sol @@ -0,0 +1,15 @@ +pragma solidity >=0.5.0 <0.6.0; + +/** + * @title InstanceAbstract + * @author Ricardo Guilherme Schmidt (Status Research & Development GmbH) + * @dev Defines instance common storage + * Important to avoid overwriting wrong storage pointers is that + * InstanceAbstract should be always the first contract at heritance. + */ +contract InstanceAbstract { + // protected zone start (InstanceAbstract vars) + InstanceAbstract public base; + // protected zone end + constructor() internal { } +} \ No newline at end of file diff --git a/contracts/deploy/InstanceFactory.sol b/contracts/deploy/InstanceFactory.sol new file mode 100644 index 0000000..0ff55bc --- /dev/null +++ b/contracts/deploy/InstanceFactory.sol @@ -0,0 +1,75 @@ +pragma solidity >=0.5.3 <0.6.0; + +import "../deploy/Instance.sol"; +import "../deploy/PrototypeRegistry.sol"; + + +/** + * @author Ricardo Guilherme Schmidt (Status Research & Development GmbH) + * @notice creates Instance and registry for updates and extensions + */ +contract InstanceFactory is PrototypeRegistry { + + InstanceAbstract public base; + + event InstanceCreated(InstanceAbstract instance); + + constructor(InstanceAbstract _base, InstanceAbstract _init, InstanceAbstract _emergency) + public + { + base = _base; + newPrototype(_base, _init, _emergency); + } + + /** + * @notice creates instance, passing msg.data to Instance constructor that delegatecalls to init + * @dev should be the same method signature of `init` function + */ + function () + external + { + Instance instance = newInstance( + base, + prototypes[address(base)].init, + msg.data, + uint256(keccak256(abi.encodePacked(msg.sender))) + ); + emit InstanceCreated(instance); + //TODO: Assembly return instance + } + + function newInstance( + InstanceAbstract _base, + InstanceAbstract _init, + bytes memory _data, + uint256 _salt + ) public returns (Instance createdContract) { + bool failed; + bytes memory _code = abi.encodePacked(type(Instance).creationCode, abi.encode(_base,_init,_data)); + assembly { + createdContract := create2(0, add(_code, 0x20), mload(_code), _salt) //deploy + failed := iszero(extcodesize(createdContract)) + } + require(!failed, "deploy failed"); + } + + function updateBase( + InstanceAbstract _base, + InstanceAbstract _init, + InstanceAbstract _emergency, + bool upgradable, + bool downgradable + ) + external + onlyController + { + newPrototype(_base, _init, _emergency); + if (upgradable) { + setUpgradable(base, _base, true); + } + if (downgradable) { + setUpgradable(_base, base, true); + } + base = _base; + } +} diff --git a/contracts/deploy/InstanceStorage.sol b/contracts/deploy/InstanceStorage.sol deleted file mode 100644 index df766b3..0000000 --- a/contracts/deploy/InstanceStorage.sol +++ /dev/null @@ -1,16 +0,0 @@ -pragma solidity ^0.4.23; - - -/** - * @title InstanceStorage - * @author Ricardo Guilherme Schmidt (Status Research & Development GmbH) - * @dev Defines kernel vars that Kernel contract share with Instance. - * Important to avoid overwriting wrong storage pointers is that - * InstanceStorage should be always the first contract at heritance. - */ -contract InstanceStorage { - // protected zone start (InstanceStorage vars) - address public kernel; - // protected zone end - constructor() internal { } -} \ No newline at end of file diff --git a/contracts/deploy/PrototypeRegistry.sol b/contracts/deploy/PrototypeRegistry.sol new file mode 100644 index 0000000..9882bb0 --- /dev/null +++ b/contracts/deploy/PrototypeRegistry.sol @@ -0,0 +1,86 @@ +pragma solidity >=0.5.0 <0.6.0; + +import "../common/Controlled.sol"; +import "./InstanceAbstract.sol"; + +contract PrototypeRegistry is Controlled { + event PrototypeAdd(InstanceAbstract _base, InstanceAbstract _init, InstanceAbstract _emergency); + event PrototypeDel(InstanceAbstract _base); + event UpgradeApprove(InstanceAbstract _baseFrom, InstanceAbstract _baseTo, bool _approve); + event ExtensionApprove(InstanceAbstract _base, InstanceAbstract _extension, bool _approve); + + struct Prototype { + InstanceAbstract init; + InstanceAbstract emergency; + } + + mapping(address => Prototype) public prototypes; + mapping(address => bool) private bases; + mapping(bytes32 => bool) private upgradable; + mapping(bytes32 => bool) private extensions; + + + function addPrototype(InstanceAbstract _base, InstanceAbstract _init, InstanceAbstract _emergency) external onlyController { + newPrototype(_base,_init,_emergency); + } + + function delPrototype(InstanceAbstract _base) external onlyController { + clearPrototype(_base); + } + + function approveUpgrade(InstanceAbstract _baseFrom, InstanceAbstract _baseTo, bool _approve) external onlyController { + setUpgradable(_baseFrom, _baseTo, _approve); + } + + function approveExtension(InstanceAbstract _base, InstanceAbstract _extension, bool _approve) external onlyController { + setExtension(_base, _extension, _approve); + } + + function isBase(InstanceAbstract _base) external view returns(bool){ + return bases[address(_base)]; + } + + function isUpgradable(InstanceAbstract _baseFrom, InstanceAbstract _baseTo) external view returns(bool){ + return upgradable[keccak256(abi.encodePacked(_baseFrom, _baseTo))]; + } + + function isExtension(InstanceAbstract _base, InstanceAbstract _extension) external view returns(bool){ + return extensions[keccak256(abi.encodePacked(_base, _extension))]; + } + + function getInit(InstanceAbstract base) external view returns (InstanceAbstract init){ + return prototypes[address(base)].init; + } + + function getEmergency(InstanceAbstract base) external view returns (InstanceAbstract emergency){ + return prototypes[address(base)].emergency; + + } + + function newPrototype(InstanceAbstract _base, InstanceAbstract _init, InstanceAbstract _emergency) internal { + prototypes[address(_base)] = Prototype(_init, _emergency); + bases[address(_base)] = true; + if(address(_emergency) != address(0)){ + bases[address(_emergency)] = true; + setUpgradable(_base, _emergency, true); + setUpgradable(_emergency, _base, true); + } + emit PrototypeAdd(_base,_init,_emergency); + } + + function clearPrototype(InstanceAbstract _base) internal { + delete prototypes[address(_base)]; + emit PrototypeDel(_base); + } + + function setUpgradable(InstanceAbstract _baseFrom, InstanceAbstract _baseTo, bool _approve) internal { + upgradable[keccak256(abi.encodePacked(_baseFrom, _baseTo))] = _approve; + emit UpgradeApprove(_baseFrom, _baseTo, _approve); + } + + function setExtension(InstanceAbstract _base, InstanceAbstract _extension, bool _approve) internal { + extensions[keccak256(abi.encodePacked(_base, _extension))] = _approve; + emit ExtensionApprove(_base,_extension,_approve); + } + +} \ No newline at end of file diff --git a/contracts/deploy/UpdatableInstance.sol b/contracts/deploy/UpdatableInstance.sol deleted file mode 100644 index 0cbb6f2..0000000 --- a/contracts/deploy/UpdatableInstance.sol +++ /dev/null @@ -1,28 +0,0 @@ -pragma solidity ^0.4.23; - -import "./Instance.sol"; - - -/** - * @title UpdatableInstance - * @author Ricardo Guilherme Schmidt (Status Research & Development GmbH) - * @dev Contract that can be updated by a call from itself. - */ -contract UpdatableInstance is Instance { - - event InstanceUpdated(address oldKernel, address newKernel); - - constructor(address _kernel) - Instance(_kernel) - public - { - - } - - function updateUpdatableInstance(address _kernel) external { - require(msg.sender == address(this)); - emit InstanceUpdated(kernel, _kernel); - kernel = _kernel; - } - -} \ No newline at end of file