diff --git a/contracts/deploy/DelegatedCall.sol b/contracts/deploy/DelegatedCall.sol new file mode 100644 index 0000000..861f751 --- /dev/null +++ b/contracts/deploy/DelegatedCall.sol @@ -0,0 +1,35 @@ +pragma solidity ^0.4.21; + + +/** + * @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 + */ +contract DelegatedCall { + /** + * @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) + } + 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 new file mode 100644 index 0000000..4ba811f --- /dev/null +++ b/contracts/deploy/Factory.sol @@ -0,0 +1,90 @@ +pragma solidity ^0.4.17; + +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; + + function Factory(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 new file mode 100644 index 0000000..6243711 --- /dev/null +++ b/contracts/deploy/Instance.sol @@ -0,0 +1,38 @@ +pragma solidity ^0.4.21; + +import "./InstanceStorage.sol"; +import "./DelegatedCall.sol"; + +/** + * @title Instance + * @author Ricardo Guilherme Schmidt (Status Research & Development GmbH) + * @dev Contract that forward everything through delegatecall to defined kernel + */ +contract Instance is InstanceStorage, DelegatedCall { + + function Instance(address _kernel) public { + kernel = _kernel; + } + + /** + * @dev delegatecall everything (but declared functions) to `_target()` + * @notice Verify `kernel()` 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; + } + + +} \ No newline at end of file diff --git a/contracts/deploy/InstanceStorage.sol b/contracts/deploy/InstanceStorage.sol new file mode 100644 index 0000000..ba84f64 --- /dev/null +++ b/contracts/deploy/InstanceStorage.sol @@ -0,0 +1,15 @@ +pragma solidity ^0.4.17; + + +/** + * @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 +} \ No newline at end of file diff --git a/contracts/deploy/UpdatableInstance.sol b/contracts/deploy/UpdatableInstance.sol new file mode 100644 index 0000000..60f6d0a --- /dev/null +++ b/contracts/deploy/UpdatableInstance.sol @@ -0,0 +1,28 @@ +pragma solidity ^0.4.17; + +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); + + function UpdatableInstance(address _kernel) + Instance(_kernel) + public + { + + } + + function updateUpdatableInstance(address _kernel) external { + require(msg.sender == address(this)); + InstanceUpdated(kernel, _kernel); + kernel = _kernel; + } + +} \ No newline at end of file