Skip to content

Commit

Permalink
feat(proxy): use Arbitrator instead of KlerosPOC and factor out unnee…
Browse files Browse the repository at this point in the history
…ded functionality
  • Loading branch information
epiqueras committed Feb 28, 2018
1 parent 8578870 commit e3ad32b
Show file tree
Hide file tree
Showing 5 changed files with 169 additions and 280 deletions.
118 changes: 29 additions & 89 deletions contracts/standard/proxy/ArbitratorVersioningProxy.sol
Original file line number Diff line number Diff line change
@@ -1,125 +1,65 @@
pragma solidity ^0.4.15;

import "kleros/contracts/KlerosPOC.sol";
import "../arbitration/Arbitrator.sol";

import "./VersioningProxy.sol";

/**
* @title ArbitratorVersioningProxy
* @author Enrique Piqueras - <epiquerass@gmail.com>
* @notice A contract derived from VersioningProxy to manage the deployment of new versions of an Arbitrator contract.
* @notice A proxy that only exposes methods in the Arbitrator spec.
*/
contract ArbitratorVersioningProxy is VersioningProxy {
/* Enums */



/* Structs */



/* Events */



/* Storage */

mapping (uint256 => address) public disputes;

/* Modifiers */


modifier onlyIfDisputeExists(uint256 _disputeID) {
require(disputes[_disputeID] != address(0));
_;
}

/* Constructor */

/**
* @notice Constructs the arbitrator versioning proxy with the first arbitrator contract version address and tags it v0.0.1.
* @param firstAddress The address of the first arbitrator contract version.
* @param _firstAddress The address of the first arbitrator contract version.
*/
function ArbitratorVersioningProxy(address firstAddress) VersioningProxy(false, "0.0.1", firstAddress) public {}

/* Fallback */



/* External */



/* External Views */


function ArbitratorVersioningProxy(address _firstAddress) VersioningProxy("0.0.1", _firstAddress) public {}

/* Public */

function createDispute(uint256 _choices, bytes _extraData) public payable returns(uint256 _disputeID) {
_disputeID = Arbitrator(implementation).createDispute(_choices, _extraData);
disputes[_disputeID] = implementation; // Remember arbitrator
return _disputeID;
}

function appeal(uint256 _disputeID, bytes _extraData) public payable onlyIfDisputeExists(_disputeID) returns(uint256 _newDisputeID) {
if (disputes[_disputeID] != implementation) // Arbitrator has been upgraded, create a new dispute in the new arbitrator
return createDispute((Arbitrator(disputes[_disputeID]).disputes(_disputeID).choices), _extraData);

Arbitrator(implementation).appeal(_disputeID, _extraData);
return _disputeID;
}

/* Public Views */



/* Internal */



/* Internal Views */



/* Private */

function bytesToBytes32(bytes b) private pure returns (bytes32) {
bytes32 out = 0;

for (uint i = 31; i > 32; i--) { // Loop from lower order to higher order bytes
out |= bytes32(b[i]) << (i * 8); // Combine with out
}

return out;
function arbitrationCost(bytes _extraData) public view returns(uint256 _fees) {
return Arbitrator(implementation).arbitrationCost(_extraData);
}

/**
* @notice On-chain handler that gets called with call data and the 'implementation' contract's return data after a call is successfully proxied.
* @dev @overwrite Proxy.
* @param sig The function signature of the called function.
* @param data The data passed into the call.
* @param retData The return data of the 'implementation' contract for the proxied call.
*/
function handleProxySuccess(bytes4 sig, bytes data, bytes retData) private {
if (sig == bytes4(keccak256("createDispute(uint256,bytes)"))) { // `createDispute` succeeded
uint256 disputeID = uint256(bytesToBytes32(retData)); // We know this is a uint256

disputes[disputeID] = implementation; // Remember which arbitrator this dispute belongs to
}
function appealCost(uint256 _disputeID, bytes _extraData) public view returns(uint256 _fees) {
return Arbitrator(implementation).appealCost(_disputeID, _extraData);
}

/* Private Views */

/**
* @notice Function for dynamically getting the 'implementation' contract address.
* @dev @overwrite Proxy.
* @param sig The function signature of the called function.
* @param data The data passed into the call.
* @return The resolved 'implementation' contract address.
*/
function getImplementation(bytes4 sig, bytes data) private view returns (address) {
if (sig == bytes4(keccak256("appeal(uint256,bytes)"))) { // `appeal` called
uint256 disputeID = uint256(bytesToBytes32(data)); // We know the first param is a uint256
address arbitrator = disputes[disputeID]; // The arbitrator this dispute belongs to

// We have changed arbitrators, create a new dispute
if (arbitrator != implementation) {
KlerosPOC oldArbitrator = KlerosPOC(arbitrator);
KlerosPOC newArbitrator = KlerosPOC(implementation);

uint256 choices = oldArbitrator.disputes(disputeID).choices;
newArbitrator.createDispute(choices, bytes(0)); // TODO: Extra Data?
}
}

// TODO: We might need to add disputeID as the first parameter of all calls to be able to resolve the right arbitrator

return implementation;
function currentRuling(uint256 _disputeID) public view onlyIfDisputeExists(_disputeID) returns(uint256 _ruling) {
return Arbitrator(disputes[_disputeID]).currentRuling(_disputeID);
}

function disputeStatus(uint256 _disputeID) public view onlyIfDisputeExists(_disputeID) returns(Arbitrator.DisputeStatus _status) {
return Arbitrator(disputes[_disputeID]).disputeStatus(_disputeID);
}
}
109 changes: 109 additions & 0 deletions contracts/standard/proxy/ExperimentalProxy.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
pragma solidity ^0.4.15;

/**
* @title ExperimentalProxy
* @author Enrique Piqueras - <epiquerass@gmail.com>
* @notice An experimental base proxy contract that forwards all calls to the 'implementation' contract and optionally keeps all storage.
*/
contract ExperimentalProxy {
/* Storage */

bool public storageIsEternal;
address public implementation;

/* Constructor */

/**
* @notice Constructs the proxy with the eternal storage flag and an initial 'implementation' contract address.
* @param _storageIsEternal Wether this contract should store all storage. I.e. Use 'delegatecall'.
* @param _implementation The initial 'implementation' contract address.
*/
function ExperimentalProxy(bool _storageIsEternal, address _implementation) public {
storageIsEternal = _storageIsEternal;
implementation = _implementation;
}

/* Fallback */

/**
* @notice The fallback function that forwards calls to the 'implementation' contract.
* @return The result of calling the requested function on the 'implementation' contract.
*/
function () payable external {
require(implementation != address(0)); // Make sure address is valid

// Store necessary data for assembly in local memory
bool _storageIsEternal = storageIsEternal;
bytes memory _data = msg.data;
address _implementation = getImplementation(msg.sig, _data);

// Return data
bytes memory _retData;

assembly {
// Start of payload raw data (skip over size slot)
let _dataPtr := add(_data, 0x20)

// Payload's size
let _dataSize := mload(_data)

// Figure out what OPCODE to use and forward call
let _result
switch _storageIsEternal
case 0 { // Not eternal, use implementation's storage
_result := call(gas, _implementation, callvalue, _dataPtr, _dataSize, 0, 0)
}
default { // Eternal, use current contract's storage
_result := delegatecall(gas, _implementation, _dataPtr, _dataSize, 0, 0)
}

// Size of the returned data
let _retSize := returndatasize

let _retPtr := mload(0x40) // Start of free memory
let _retDataPtr := add(_retPtr, 0x20) // Make space for 'bytes' size

// Build `_retData` 'bytes'
mstore(_retPtr, _retSize) // Copy size
returndatacopy(_retDataPtr, 0, _retSize) // Copy returned data

// Figure out wether to revert or continue with the returned data
switch _result
case 0 { // Error
revert(_retDataPtr, _retSize)
}
default { // Success
_retData := _retPtr
}
}

// Call on-chain handler
handleProxySuccess(msg.sig, _data, _retData);

assembly {
return(add(_retData, 0x20), mload(_retData)) // Return returned data
}
}

/* Private */

/**
* @notice On-chain handler that gets called with call data and the 'implementation' contract's return data after a call is successfully proxied.
* @dev Overwrite this function to handle the results of proxied calls in this contract.
* @param _sig The function signature of the called function.
* @param _data The data passed into the call.
* @param _retData The return data of the 'implementation' contract for the proxied call.
*/
function handleProxySuccess(bytes4 _sig, bytes _data, bytes _retData) private {}

/* Private Views */

/**
* @notice Function for dynamically getting the 'implementation' contract address.
* @dev Overwrite this function to implement custom resolving logic based on the function being called and the data passed in.
* @param _sig The function signature of the called function.
* @param _data The data passed into the call.
* @return The resolved 'implementation' contract address.
*/
function getImplementation(bytes4 _sig, bytes _data) private view returns(address _implementation) { return implementation; }
}
93 changes: 3 additions & 90 deletions contracts/standard/proxy/Proxy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,107 +3,20 @@ pragma solidity ^0.4.15;
/**
* @title Proxy
* @author Enrique Piqueras - <epiquerass@gmail.com>
* @notice A base proxy contract that forwards all calls to the 'implementation' contract and optionally keeps all storage.
* @notice A base proxy contract.
*/
contract Proxy {
/* Storage */

bool public storageIsEternal;
address public implementation;

/* Constructor */

/**
* @notice Constructs the proxy with the eternal storage flag and an initial 'implementation' contract address.
* @param _storageIsEternal Wether this contract should store all storage. I.e. Use 'delegatecall'.
* @notice Constructs the proxy with the initial 'implementation' contract address.
* @param _implementation The initial 'implementation' contract address.
*/
function Proxy(bool _storageIsEternal, address _implementation) public {
storageIsEternal = _storageIsEternal;
function Proxy(address _implementation) public {
implementation = _implementation;
}

/* Fallback */

/**
* @notice The fallback function that forwards calls to the 'implementation' contract.
* @return The result of calling the requested function on the 'implementation' contract.
*/
function () payable external {
require(implementation != address(0)); // Make sure address is valid

// Store necessary data for assembly in local memory
bool _storageIsEternal = storageIsEternal;
bytes memory data = msg.data;
address _implementation = getImplementation(msg.sig, data);

// Return data
bytes memory retData;

assembly {
// Start of payload raw data (skip over size slot)
let dataPtr := add(data, 0x20)

// Payload's size
let dataSize := mload(data)

// Figure out what OPCODE to use and forward call
let result
switch _storageIsEternal
case 0 { // Not eternal, use implementation's storage
result := call(gas, _implementation, callvalue, dataPtr, dataSize, 0, 0)
}
default { // Eternal, use current contract's storage
result := delegatecall(gas, _implementation, dataPtr, dataSize, 0, 0)
}

// Size of the returned data
let retSize := returndatasize

let retPtr := mload(0x40) // Start of free memory
let retDataPtr := add(retPtr, 0x20) // Make space for 'bytes' size

// Build `retData` 'bytes'
mstore(retPtr, retSize) // Copy size
returndatacopy(retDataPtr, 0, retSize) // Copy returned data

// Figure out wether to revert or continue with the returned data
switch result
case 0 { // Error
revert(retDataPtr, retSize)
}
default { // Success
retData := retPtr
}
}

// Call on-chain handler
handleProxySuccess(msg.sig, data, retData);

assembly {
return(add(retData, 0x20), mload(retData)) // Return returned data
}
}

/* Private */

/**
* @notice On-chain handler that gets called with call data and the 'implementation' contract's return data after a call is successfully proxied.
* @dev Overwrite this function to handle the results of proxied calls in this contract.
* @param sig The function signature of the called function.
* @param data The data passed into the call.
* @param retData The return data of the 'implementation' contract for the proxied call.
*/
function handleProxySuccess(bytes4 sig, bytes data, bytes retData) private {}

/* Private Views */

/**
* @notice Function for dynamically getting the 'implementation' contract address.
* @dev Overwrite this function to implement custom resolving logic based on the function being called and the data passed in.
* @param sig The function signature of the called function.
* @param data The data passed into the call.
* @return The resolved 'implementation' contract address.
*/
function getImplementation(bytes4 sig, bytes data) private view returns (address) { return implementation; }
}
Loading

0 comments on commit e3ad32b

Please sign in to comment.