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

Aragon Court POC implementation #6

Merged
merged 27 commits into from Mar 25, 2019
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
c26f6e2
Copy contracts
izqui Feb 19, 2019
7ff426f
Implement ERC900
izqui Feb 19, 2019
3d91f76
Remove untested Kleros code
izqui Feb 20, 2019
500d3d1
Heartbeat wip
izqui Feb 21, 2019
3297fce
Court state machine: activate, deactivate, withdraw
izqui Feb 22, 2019
fee8c87
Address review comments
izqui Mar 1, 2019
6545acc
Disputes scaffolding
izqui Mar 1, 2019
c405771
Rebase sortition
izqui Mar 1, 2019
9cf0334
Juror drafts
izqui Mar 1, 2019
3ca94bb
WIP: dispute voting
izqui Mar 2, 2019
e167cbc
Fix stack too deep issues with voting
izqui Mar 4, 2019
6d0b322
Clean up
izqui Mar 18, 2019
fe1abf9
Rename for clarity
izqui Mar 18, 2019
dd687db
Remove ingress queue in favor of updates
izqui Mar 18, 2019
1f6adae
Implement CourtConfig and track juror amount of tokens at stake
izqui Mar 19, 2019
6a5737d
Start testing dispute lifecycle
izqui Mar 19, 2019
46ceb8f
Test commit and reveal happy case + minor improvements
izqui Mar 20, 2019
146f788
Implement appeals, handle updates after exit term
izqui Mar 21, 2019
5896c12
Round slashing settlement
izqui Mar 22, 2019
9d5d795
Reward settle and refund fees in settlement if no coherent jurors
izqui Mar 22, 2019
19e962c
Bypass OOG error and basic tests for slashing and rewards
izqui Mar 22, 2019
8327a1f
Fix heartbeat transitioning one extra term
izqui Mar 22, 2019
323f362
Properly order functions and notice strings
izqui Mar 22, 2019
61cfe77
Address review comments
izqui Mar 23, 2019
c44ba3b
Add readme
izqui Mar 25, 2019
778ab8b
Improve readme
izqui Mar 25, 2019
3824223
Add license
izqui Mar 25, 2019
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
33 changes: 33 additions & 0 deletions contracts/Agreement.sol
@@ -0,0 +1,33 @@
pragma solidity ^0.4.15;

import "./standards/arbitration/Arbitrable.sol";


contract Agreement is Arbitrable /* AragonApp/Trigger */ {
address[] parties;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably be 2, proposer and challenger, and challenger won't be known in advance. Let's discuss it.


// TODO: Probably needs to be moved into an 'initialize()' function at some point
constructor(address _court, address[] _parties)
public
Arbitrable(_court) {

parties = _parties;
}

function canSubmitEvidence(uint256 _disputeId, address _submitter) public view returns (bool) {
// TODO: should check court to see whether evidence can be submitted for this particular dispute at this point
uint256 partiesLength = parties.length;
for (uint256 i = 0; i < partiesLength; i++) {
if (parties[i] == msg.sender) {
return true;
}
}
}

/**
* @dev Execute a ruling of a dispute.
* @param _disputeId Id of the dispute in the Court contract.
* @param _ruling Ruling given by the arbitrator. Note that 0 is reserved for "Not able/wanting to make a decision".
*/
function _executeRuling(uint256 _disputeId, uint256 _ruling) internal;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a TODO, right? Or is it going to be abstract too?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it makes sense for Agreement to be an abstract contract and require contracts that inherit from it to implement the function.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But this is already in Arbitrable. Do we need to repeat it here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could actually implement the management of stake locks in this contract: #13

}
921 changes: 921 additions & 0 deletions contracts/Court.sol

Large diffs are not rendered by default.

21 changes: 21 additions & 0 deletions contracts/lib/ArrayUtils.sol
@@ -0,0 +1,21 @@
pragma solidity ^0.4.24;


library ArrayUtils {
function deleteItem(address[] storage self, address item) internal returns (bool) {
uint256 length = self.length;
for (uint256 i = 0; i < length; i++) {
if (self[i] == item) {
uint256 newLength = self.length - 1;
if (i != newLength) {
self[i] = self[newLength];
}

delete self[newLength];
self.length = newLength;

return true;
}
}
}
}
42 changes: 33 additions & 9 deletions contracts/HexSumTree.sol → contracts/lib/HexSumTree.sol
Expand Up @@ -21,7 +21,8 @@ library HexSumTree {

string private constant ERROR_SORTITION_OUT_OF_BOUNDS = "SUM_TREE_SORTITION_OUT_OF_BOUNDS";
string private constant ERROR_NEW_KEY_NOT_ADJACENT = "SUM_TREE_NEW_KEY_NOT_ADJACENT";
string private constant ERROR_UPDATE_OVERFLOW = "SUM_UPDATE_OVERFLOW";
string private constant ERROR_UPDATE_OVERFLOW = "SUM_TREE_UPDATE_OVERFLOW";
string private constant ERROR_INEXISTENT_ITEM = "SUM_TREE_INEXISTENT_ITEM";

function init(Tree storage self) internal {
self.rootDepth = INSERTION_DEPTH + 1;
Expand All @@ -32,7 +33,9 @@ library HexSumTree {
uint256 key = self.nextKey;
self.nextKey = nextKey(key);

_set(self, key, value);
if (value > 0) {
_set(self, key, value);
}

return key;
}
Expand All @@ -42,20 +45,37 @@ library HexSumTree {
_set(self, key, value);
}

function sortition(Tree storage self, uint256 value) internal view returns (uint256 key) {
function update(Tree storage self, uint256 key, uint256 delta, bool positive) internal {
require(key < self.nextKey, ERROR_INEXISTENT_ITEM);

uint256 oldValue = self.nodes[INSERTION_DEPTH][key];
self.nodes[INSERTION_DEPTH][key] = positive ? oldValue + delta : oldValue - delta;

updateSums(self, key, delta, positive);
}

function sortition(Tree storage self, uint256 value) internal view returns (uint256 key, uint256 nodeValue) {
require(totalSum(self) > value, ERROR_SORTITION_OUT_OF_BOUNDS);

return _sortition(self, value, BASE_KEY, self.rootDepth);
}

function randomSortition(Tree storage self, uint256 seed) internal view returns (uint256 key, uint256 nodeValue) {
return _sortition(self, seed % totalSum(self), BASE_KEY, self.rootDepth);
}

function _set(Tree storage self, uint256 key, uint256 value) private {
uint256 oldValue = self.nodes[INSERTION_DEPTH][key];
self.nodes[INSERTION_DEPTH][key] = value;

updateSums(self, key, int256(value - oldValue));
if (value > oldValue) {
updateSums(self, key, value - oldValue, true);
} else if (value < oldValue) {
updateSums(self, key, oldValue - value, false);
}
}

function _sortition(Tree storage self, uint256 value, uint256 node, uint256 depth) private view returns (uint256 key) {
function _sortition(Tree storage self, uint256 value, uint256 node, uint256 depth) private view returns (uint256 key, uint256 nodeValue) {
uint256 checkedValue = 0; // Can optimize by having checkedValue = value - remainingValue

uint256 checkingLevel = depth - 1;
Expand Down Expand Up @@ -88,13 +108,13 @@ library HexSumTree {
if (checkedValue + nodeSum <= value) { // not reached yet, move to next child
checkedValue += nodeSum;
} else { // value reached
return checkingNode;
return (checkingNode, nodeSum);
}
}
// Invariant: this point should never be reached
}

function updateSums(Tree storage self, uint256 key, int256 delta) private {
function updateSums(Tree storage self, uint256 key, uint256 delta, bool positive) private {
uint256 newRootDepth = sharedPrefix(self.rootDepth, key);

if (self.rootDepth != newRootDepth) {
Expand All @@ -109,10 +129,10 @@ library HexSumTree {
ancestorKey = ancestorKey & mask;

// Invariant: this will never underflow.
self.nodes[i][ancestorKey] = uint256(int256(self.nodes[i][ancestorKey]) + delta);
self.nodes[i][ancestorKey] = positive ? self.nodes[i][ancestorKey] + delta : self.nodes[i][ancestorKey] - delta;
}
// it's only needed to check the last one, as the sum increases going up through the tree
require(delta <= 0 || self.nodes[self.rootDepth][ancestorKey] >= uint256(delta), ERROR_UPDATE_OVERFLOW);
require(!positive || self.nodes[self.rootDepth][ancestorKey] >= delta, ERROR_UPDATE_OVERFLOW);
}

function totalSum(Tree storage self) internal view returns (uint256) {
Expand All @@ -123,6 +143,10 @@ library HexSumTree {
return self.nodes[depth][key];
}

function getItem(Tree storage self, uint256 key) internal view returns (uint256) {
return self.nodes[INSERTION_DEPTH][key];
}

function nextKey(uint256 fromKey) private pure returns (uint256) {
return fromKey + 1;
}
Expand Down
53 changes: 53 additions & 0 deletions contracts/standards/arbitration/Arbitrable.sol
@@ -0,0 +1,53 @@
pragma solidity ^0.4.15;

import "./IArbitrable.sol";
import "../erc165/ERC165.sol";


contract Arbitrable is IArbitrable, ERC165 {
address public court; // TODO: replace for ICourt or Court interface

bytes4 private constant ERC165_INTERFACE_ID = 0x01ffc9a7;
bytes4 private constant ARBITRABLE_INTERFACE_ID = 0xabababab; // TODO: interface id

string private constant ERROR_NOT_COURT = "ARBITRABLE_NOT_COURT";
string private constant ERROR_CANNOT_SUBMIT_EVIDENCE = "ARBITRABLE_CANNOT_SUBMIT_EVIDENCE";
izqui marked this conversation as resolved.
Show resolved Hide resolved

/** @dev Constructor. Choose the arbitrator.
* @param _court The address of the court that arbitrates the contract.
*/
constructor(address _court) public {
court = _court;
}

/**
* @dev Give a ruling for a dispute. Must be called by the arbitrator.
* The purpose of this function is to ensure that the address calling it has the right to rule on the contract.
* @param _disputeId Id of the dispute in the Court contract.
* @param _ruling Ruling given by the arbitrator. Note that 0 is reserved for "Not able/wanting to make a decision".
*/
function rule(uint256 _disputeId, uint256 _ruling) external {
require(msg.sender == court, ERROR_NOT_COURT);

_executeRuling(_disputeId, _ruling);

emit CourtRuling(msg.sender, _disputeId, _ruling);
}

function submitEvidence(uint256 _disputeId, bytes _evidence) external {
require(canSubmitEvidence(_disputeId, msg.sender), ERROR_CANNOT_SUBMIT_EVIDENCE);

emit NewEvidence(court, _disputeId, msg.sender, _evidence);
}

function supportsInterface(bytes4 _interfaceId) external pure returns (bool) {
return _interfaceId == ARBITRABLE_INTERFACE_ID || _interfaceId == ERC165_INTERFACE_ID;
}

/**
* @dev Execute a ruling of a dispute.
* @param _disputeId Id of the dispute in the Court contract.
* @param _ruling Ruling given by the arbitrator. Note that 0 is reserved for "Not able/wanting to make a decision".
*/
function _executeRuling(uint256 _disputeId, uint256 _ruling) internal;
}
43 changes: 43 additions & 0 deletions contracts/standards/arbitration/IArbitrable.sol
@@ -0,0 +1,43 @@
pragma solidity ^0.4.15;


interface IArbitrable {
/**
* @dev To be emmited when a dispute is created to link the correct meta-evidence to the disputeId
* @param _court The court resolving the dispute.
* @param _disputeId Id of the dispute in the Court.
* @param _baseEvidence Base evidence or location of the base evidence being submitted
*/
event NewDispute(address indexed _court, uint256 indexed _disputeId, bytes _baseEvidence);

/**
* @dev To be raised when evidence are submitted. Should point to the ressource (evidences are not to be stored on chain due to gas considerations).
* @param _court The court resolving the dispute.
* @param _disputeId Id of the dispute in the Court.
* @param _submitter The address of the entity submitting the evidence.
* @param _evidence Evidence or location of the evidence being submitted
*/
event NewEvidence(address indexed _court, uint256 indexed _disputeId, address indexed _submitter, bytes _evidence);

/**
* @dev To be raised when a ruling is given.
* @param _court The court giving the ruling.
* @param _disputeId Id of the dispute in the Court.
* @param _ruling The ruling which was given.
*/
event CourtRuling(address indexed _court, uint256 indexed _disputeId, uint256 _ruling);

/** @dev Give a ruling for a dispute. Must be called by the court.
* The purpose of this function is to ensure that the address calling it has the right to rule on the contract.
* @param _disputeId Id of the dispute in the Court.
* @param _ruling Ruling given by the court. Note that 0 is reserved for "Not able/wanting to make a decision".
*/
function rule(uint256 _disputeId, uint256 _ruling) external;

/**
* @param _disputeId Id of the dispute in the Court
* @param _submitter address of the entity that wishes to submit evidence
* @return bool whether the submitter is allowed to submit evidence for the dispute
*/
function canSubmitEvidence(uint256 _disputeId, address _submitter) public view returns (bool);
}
6 changes: 6 additions & 0 deletions contracts/standards/erc165/ERC165.sol
@@ -0,0 +1,6 @@
pragma solidity ^0.4.24;


interface ERC165 {
function supportsInterface(bytes4 interfaceId) external pure returns (bool);
}
17 changes: 17 additions & 0 deletions contracts/standards/erc900/ERC900.sol
@@ -0,0 +1,17 @@
pragma solidity ^0.4.24;

// Interface for ERC900: https://eips.ethereum.org/EIPS/eip-900
interface ERC900 {
event Staked(address indexed user, uint256 amount, uint256 total, bytes data);
event Unstaked(address indexed user, uint256 amount, uint256 total, bytes data);

function stake(uint256 amount, bytes data) external;
function stakeFor(address user, uint256 amount, bytes data) external;
function unstake(uint256 amount, bytes data) external;

function totalStakedFor(address addr) external view returns (uint256);
function totalStaked() external view returns (uint256);
function token() external view returns (address);

function supportsHistory() external pure returns (bool);
}
42 changes: 42 additions & 0 deletions contracts/standards/rng/RNG.sol
@@ -0,0 +1,42 @@
/**
* @title Random Number Generator Standard
* @author Clément Lesaege - <clement@lesaege.com>
*
*/

pragma solidity ^0.4.15;

contract RNG{

/** @dev Contribute to the reward of a random number.
* @param _block Block the random number is linked to.
*/
function contribute(uint _block) public payable;

/** @dev Request a random number.
* @param _block Block linked to the request.
*/
function requestRN(uint _block) public payable {
contribute(_block);
}

/** @dev Get the random number.
* @param _block Block the random number is linked to.
* @return RN Random Number. If the number is not ready or has not been required 0 instead.
*/
function getRN(uint _block) public returns (uint RN);

/** @dev Get a uncorrelated random number. Act like getRN but give a different number for each sender.
* This is to prevent users from getting correlated numbers.
* @param _block Block the random number is linked to.
* @return RN Random Number. If the number is not ready or has not been required 0 instead.
*/
function getUncorrelatedRN(uint _block) public returns (uint RN) {
uint baseRN = getRN(_block);
if (baseRN == 0)
return 0;
else
return uint(keccak256(msg.sender,baseRN));
}

}