Skip to content

Commit

Permalink
Court: address PR #45 comments (final appeals)
Browse files Browse the repository at this point in the history
  • Loading branch information
ßingen committed Jun 7, 2019
1 parent 8294a6b commit 6cf37e4
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 21 deletions.
34 changes: 20 additions & 14 deletions contracts/Court.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ contract Court is ERC900, ApproveAndCallFallBack, ICRVotingOwner {
using SafeMath for uint256;

uint256 internal constant MAX_JURORS_PER_BATCH = 10; // to cap gas used on draft
uint256 internal constant MAX_DRAFT_ROUNDS = 4; // before the final appeal
uint256 internal constant MAX_REGULAR_APPEAL_ROUNDS = 4; // before the final appeal
// TODO: move all other constants up here

struct Account {
Expand Down Expand Up @@ -489,10 +489,10 @@ contract Court is ERC900, ApproveAndCallFallBack, ICRVotingOwner {
address juror = jurorsByTreeId[jurorKeys[i]];

// Account storage jurorAccount = accounts[juror]; // Hitting stack too deep
// account also for pending final rounds
uint256 newAtStake = accounts[juror].atStakeTokens + _finalRoundsLockedAmount(accounts[juror]) + _pct4(jurorMinStake, config.penaltyPct);
uint256 newAtStake = accounts[juror].atStakeTokens + _pct4(jurorMinStake, config.penaltyPct);
// Only select a juror if their stake is greater than or equal than the amount of tokens that they can lose, otherwise skip it
if (stakes[i] >= newAtStake) {
// account also for pending final rounds
if (stakes[i] >= newAtStake + _finalRoundsLockedAmount(accounts[juror])) {
accounts[juror].atStakeTokens = newAtStake;
// check repeated juror, we assume jurors come ordered from tree search
if (round.nextJurorIndex > 0 && round.jurors[round.nextJurorIndex - 1] == juror) {
Expand Down Expand Up @@ -534,7 +534,7 @@ contract Court is ERC900, ApproveAndCallFallBack, ICRVotingOwner {

uint256 roundId;
uint32 voteId;
if (_roundId == MAX_DRAFT_ROUNDS - 1) { // roundId starts at 0
if (_roundId == MAX_REGULAR_APPEAL_ROUNDS - 1) { // roundId starts at 0
(roundId, voteId, appealJurorNumber) = _finalAdjudicationRound(_disputeId, appealDraftTermId);
} else { // no need for more checks, as final appeal won't ever be in Appealable state, so it would never reach here (first check would fail)
appealJurorNumber = 2 * currentRound.jurorNumber + 1; // J' = 2J + 1
Expand All @@ -555,7 +555,7 @@ contract Court is ERC900, ApproveAndCallFallBack, ICRVotingOwner {
Dispute storage dispute = disputes[_disputeId];
dispute.state = DisputeState.Executed;

uint8 winningRuling = dispute.rounds[_roundId].winningRuling;
uint8 winningRuling = _getWinningRuling(dispute);

dispute.subject.rule(_disputeId, uint256(winningRuling));

Expand Down Expand Up @@ -586,7 +586,7 @@ contract Court is ERC900, ApproveAndCallFallBack, ICRVotingOwner {
round.coherentJurors = uint32(coherentJurors);

uint256 slashedTokens;
if (_roundId < MAX_DRAFT_ROUNDS) {
if (_roundId < MAX_REGULAR_APPEAL_ROUNDS) {
slashedTokens = _settleRegularRoundSlashing(round, config.penaltyPct, winningRuling);
_payFees(config.feeToken, msg.sender, config.settleFee * round.jurorNumber, config.governanceFeeShare);
} else {
Expand Down Expand Up @@ -667,6 +667,7 @@ contract Court is ERC900, ApproveAndCallFallBack, ICRVotingOwner {
AdjudicationRound storage round = dispute.rounds[_roundId];
JurorState storage jurorState = round.jurorSlotStates[_juror];

require(_roundId < MAX_REGULAR_APPEAL_ROUNDS, ERROR_INVALID_ADJUDICATION_ROUND);
require(round.settledPenalties, ERROR_ROUND_NOT_SETTLED);
require(jurorState.weight > 0, ERROR_INVALID_JUROR);
require(!jurorState.rewarded, ERROR_JUROR_ALREADY_REWARDED);
Expand Down Expand Up @@ -712,20 +713,20 @@ contract Court is ERC900, ApproveAndCallFallBack, ICRVotingOwner {
function _settleFinalRound(address _juror, Account storage _account, FinalAdjudicationRound _finalAdjudicationRound) internal {
uint256 disputeId = _finalAdjudicationRound.disputeId;
Dispute storage dispute = disputes[disputeId];
AdjudicationRound storage round = dispute.rounds[MAX_DRAFT_ROUNDS];
AdjudicationRound storage round = dispute.rounds[MAX_REGULAR_APPEAL_ROUNDS];
CourtConfig storage config = courtConfigs[terms[round.draftTermId].courtConfigId]; // safe to use directly as it is a past term

require(round.settledPenalties, ERROR_ROUND_NOT_SETTLED);

uint256 coherentJurors = round.coherentJurors;
uint8 jurorRuling = voting.getCastVote(round.voteId, _juror);
uint256 weight = _getJurorWeight(disputeId, MAX_DRAFT_ROUNDS, _juror);
uint256 weight = _getJurorWeight(disputeId, MAX_REGULAR_APPEAL_ROUNDS, _juror);

if (jurorRuling == round.winningRuling) {
_assignTokens(jurorToken, _juror, round.slashedTokens * weight / coherentJurors);
_payFees(config.feeToken, _juror, config.jurorFee * round.jurorNumber * weight / coherentJurors, config.governanceFeeShare);

emit RewardSettled(disputeId, MAX_DRAFT_ROUNDS, _juror);
emit RewardSettled(disputeId, MAX_REGULAR_APPEAL_ROUNDS, _juror);
} else {
uint256 penalty = _finalAdjudicationRound.lockedStakePerJuror * weight;
uint64 slashingUpdateTermId = termId + 1;
Expand Down Expand Up @@ -809,9 +810,14 @@ contract Court is ERC900, ApproveAndCallFallBack, ICRVotingOwner {
/**
* @dev Assumes that it is always called ensuring the term
*/
function unlockedBalanceOf(address _addr) public view returns (uint256) {
function unlockedBalanceOf(address _addr) public view returns (uint256 balance) {
Account storage account = accounts[_addr];
return account.balances[jurorToken].sub(account.atStakeTokens).sub(_finalRoundsLockedAmount(account));
balance = account.balances[jurorToken].sub(account.atStakeTokens);
uint256 finalRoundsLockedAmount = _finalRoundsLockedAmount(account);
if (balance < finalRoundsLockedAmount) {
return 0;
}
balance = balance - finalRoundsLockedAmount;
}

function _finalRoundsLockedAmount(Account storage _account) internal returns (uint256) {
Expand Down Expand Up @@ -858,7 +864,7 @@ contract Court is ERC900, ApproveAndCallFallBack, ICRVotingOwner {

function _getJurorWeight(uint256 _disputeId, uint256 _roundId, address _juror) internal view returns (uint256) {
// for the final round
if (_roundId == MAX_DRAFT_ROUNDS) {
if (_roundId == MAX_REGULAR_APPEAL_ROUNDS) {
return sumTree.getItemPast(accounts[_juror].sumTreeId, disputes[_disputeId].rounds[_roundId].draftTermId) / jurorMinStake;
}

Expand Down Expand Up @@ -982,7 +988,7 @@ contract Court is ERC900, ApproveAndCallFallBack, ICRVotingOwner {
return AdjudicationState.Commit;
} else if (_termId < appealStart) {
return AdjudicationState.Reveal;
} else if (_termId < appealEnd && _roundId < MAX_DRAFT_ROUNDS) {
} else if (_termId < appealEnd && _roundId < MAX_REGULAR_APPEAL_ROUNDS) {
return AdjudicationState.Appealable;
} else {
return AdjudicationState.Ended;
Expand Down
4 changes: 2 additions & 2 deletions contracts/test/CourtMock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,8 @@ contract CourtMock is Court {
return MAX_JURORS_PER_BATCH;
}

function getMaxDraftRounds() public pure returns (uint256) {
return MAX_DRAFT_ROUNDS;
function getMaxRegularAppealRounds() public pure returns (uint256) {
return MAX_REGULAR_APPEAL_ROUNDS;
}

function getAdjudicationState(uint256 _disputeId, uint256 _roundId, uint64 _termId) public view returns (AdjudicationState) {
Expand Down
15 changes: 10 additions & 5 deletions test/court-final-appeal.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ contract('Court: final appeal', ([ poor, rich, governor, juror1, juror2, juror3,
const NO_DATA = ''
const ZERO_ADDRESS = '0x' + '00'.repeat(20)
let MAX_JURORS_PER_BATCH
let MAX_DRAFT_ROUNDS
let MAX_REGULAR_APPEAL_ROUNDS

const termDuration = 10
const firstTermStart = 1
Expand All @@ -61,6 +61,7 @@ contract('Court: final appeal', ([ poor, rich, governor, juror1, juror2, juror3,
const REWARD_SETTLED_EVENT = 'RewardSettled'

const ERROR_INVALID_ADJUDICATION_STATE = 'COURT_INVALID_ADJUDICATION_STATE'
const ERROR_INVALID_ADJUDICATION_ROUND = 'COURT_INVALID_ADJUDICATION_ROUND'

const SALT = soliditySha3('passw0rd')

Expand Down Expand Up @@ -107,7 +108,7 @@ contract('Court: final appeal', ([ poor, rich, governor, juror1, juror2, juror3,
)

MAX_JURORS_PER_BATCH = (await this.court.getMaxJurorsPerBatch.call()).toNumber()
MAX_DRAFT_ROUNDS = (await this.court.getMaxDraftRounds.call()).toNumber()
MAX_REGULAR_APPEAL_ROUNDS = (await this.court.getMaxRegularAppealRounds.call()).toNumber()

await this.court.mock_setBlockNumber(startBlock)

Expand Down Expand Up @@ -172,7 +173,7 @@ contract('Court: final appeal', ([ poor, rich, governor, juror1, juror2, juror3,
const moveForwardToFinalRound = async () => {
await passTerms(2) // term = 3, dispute init

for (let roundId = 0; roundId < MAX_DRAFT_ROUNDS; roundId++) {
for (let roundId = 0; roundId < MAX_REGULAR_APPEAL_ROUNDS; roundId++) {
const roundJurors = (2**roundId) * jurorNumber + 2**roundId - 1
// draft
await draftAdjudicationRound(roundJurors)
Expand Down Expand Up @@ -213,7 +214,7 @@ contract('Court: final appeal', ([ poor, rich, governor, juror1, juror2, juror3,
await passTerms(revealTerms)

// appeal
await assertRevert(this.court.appealRuling(disputeId, MAX_DRAFT_ROUNDS), ERROR_INVALID_ADJUDICATION_STATE)
await assertRevert(this.court.appealRuling(disputeId, MAX_REGULAR_APPEAL_ROUNDS), ERROR_INVALID_ADJUDICATION_STATE)
})

context('Rewards and slashes', () => {
Expand Down Expand Up @@ -245,12 +246,16 @@ contract('Court: final appeal', ([ poor, rich, governor, juror1, juror2, juror3,
await passTerms(revealTerms)

// settle
for (let roundId = 0; roundId <= MAX_DRAFT_ROUNDS; roundId++) {
for (let roundId = 0; roundId <= MAX_REGULAR_APPEAL_ROUNDS; roundId++) {
const receiptPromise = this.court.settleRoundSlashing(disputeId, roundId)
await assertLogs(receiptPromise, ROUND_SLASHING_SETTLED_EVENT)
}
})

it('fails trying to call regular rounds settle reward', async () => {
await assertRevert(this.court.settleReward(disputeId, MAX_REGULAR_APPEAL_ROUNDS, jurors[0], { from: jurors[0] }), ERROR_INVALID_ADJUDICATION_ROUND)
})

it('winning jurors get reward', async () => {
for (let i = 0; i < winningJurors; i++) {
const tokenBalance = (await this.anj.balanceOf(jurors[i])).toNumber()
Expand Down

0 comments on commit 6cf37e4

Please sign in to comment.