update instance factory

This commit is contained in:
Ricardo Guilherme Schmidt 2019-02-14 02:18:08 -02:00
parent 0e01f27410
commit 2a0495f9f7
No known key found for this signature in database
GPG Key ID: BFB3F5C8ED618A94
8 changed files with 223 additions and 180 deletions

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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))
{ }
}

View File

@ -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 { }
}

View File

@ -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;
}
}

View File

@ -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 { }
}

View File

@ -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);
}
}

View File

@ -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;
}
}