-
Notifications
You must be signed in to change notification settings - Fork 62
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(proxy): use Arbitrator instead of KlerosPOC and factor out unnee…
…ded functionality
- Loading branch information
Showing
5 changed files
with
169 additions
and
280 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.