update instance factory
This commit is contained in:
parent
0e01f27410
commit
2a0495f9f7
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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))
|
||||
{ }
|
||||
|
||||
}
|
|
@ -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 { }
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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 { }
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue