Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Byzantium] Implement support for dynamic returns in delegatecall proxies #102

Merged
merged 5 commits into from Oct 13, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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);
}
}