Skip to content

Commit

Permalink
Merge pull request #102 from aragon/metro-forwarder
Browse files Browse the repository at this point in the history
[Byzantium] Implement support for dynamic returns in delegatecall proxies
  • Loading branch information
izqui committed Oct 13, 2017
2 parents 3ffb982 + e9282d6 commit ee3679b
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 17 deletions.
11 changes: 3 additions & 8 deletions contracts/apps/AppProxy.sol
@@ -1,22 +1,17 @@
pragma solidity 0.4.15;

import "./AppStorage.sol";
import "../common/DelegateProxy.sol";

contract AppProxy is AppStorage {
contract AppProxy is AppStorage, DelegateProxy {
function AppProxy(Kernel _kernel, bytes32 _appId) {
kernel = _kernel;
appId = _appId;
}

function () payable public {
uint32 len = 320; // 10 return size
address target = kernel.getAppCode(appId);
require(target > 0); // if app code hasn't been set yet, don't call
assembly {
calldatacopy(0x0, 0x0, calldatasize)
let result := delegatecall(sub(gas, 10000), target, 0x0, calldatasize, 0, len)
switch result case 0 { invalid() }
return (0, len)
}
delegatedFwd(target, msg.data);
}
}
40 changes: 40 additions & 0 deletions contracts/common/DelegateProxy.sol
@@ -0,0 +1,40 @@
pragma solidity 0.4.15;

contract DelegateProxy {
bool constant IS_BYZANTIUM = false;

// TODO: Remove pre-byzantium logic after tools have new opcodes

/**
* @dev Performs a delegatecall and returns whatever the delegatecall returned (entire context execution will return!)
* @param _dst Destination address to perform the delegatecall
* @param _calldata Calldata for the delegatecall
*/
function delegatedFwd(address _dst, bytes _calldata) internal {
uint useByzantiumOpcodes = IS_BYZANTIUM ? 1 : 0;
assembly {
switch extcodesize(_dst) case 0 { revert(0, 0) }

switch useByzantiumOpcodes
case 0 {
// This code block will be removed
let len := 4096
let result := delegatecall(sub(gas, 10000), _dst, add(_calldata, 0x20), mload(_calldata), 0, len)
switch result case 0 { invalid() }
return (0, len)
}
default {
let result := delegatecall(sub(gas, 10000), _dst, add(_calldata, 0x20), mload(_calldata), 0, 0)
let size := returndatasize

let ptr := mload(0x40)
returndatacopy(ptr, 0, size)

// revert instead of invalid() bc if the underlying call failed with invalid() it already wasted gas.
// if the call returned error data, forward it
switch result case 0 { revert(ptr, size) }
default { return(ptr, size) }
}
}
}
}
12 changes: 3 additions & 9 deletions contracts/kernel/KernelProxy.sol
@@ -1,8 +1,9 @@
pragma solidity 0.4.15;

import "./KernelStorage.sol";
import "../common/DelegateProxy.sol";

contract KernelProxy is KernelStorage {
contract KernelProxy is KernelStorage, DelegateProxy {
/**
* @dev KernelProxy is a proxy contract to a kernel implementation. The implementation
* can update the reference, which effectively upgrades the contract
Expand All @@ -17,13 +18,6 @@ contract KernelProxy is KernelStorage {
* @return Any bytes32 value the implementation returns
*/
function () payable public {
uint32 len = 32; // only 1 return (can be increased if needed)
address target = kernelImpl;
assembly {
calldatacopy(0x0, 0x0, calldatasize)
let result := delegatecall(sub(gas, 10000), target, 0x0, calldatasize, 0, len)
switch result case 0 { invalid() }
return (0, len)
}
delegatedFwd(kernelImpl, msg.data);
}
}

0 comments on commit ee3679b

Please sign in to comment.