Skip to content

Commit 6b24daa

Browse files
feat(KC): add foundry test file
1 parent e8e48f4 commit 6b24daa

12 files changed

+2814
-16
lines changed

contracts/src/arbitration/KlerosCoreBase.sol

+10-8
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ abstract contract KlerosCoreBase is IArbitratorV2 {
113113
event AppealDecision(uint256 indexed _disputeID, IArbitrableV2 indexed _arbitrable);
114114
event Draw(address indexed _address, uint256 indexed _disputeID, uint256 _roundID, uint256 _voteID);
115115
event CourtCreated(
116-
uint256 indexed _courtID,
116+
uint96 indexed _courtID,
117117
uint96 indexed _parent,
118118
bool _hiddenVotes,
119119
uint256 _minStake,
@@ -237,16 +237,18 @@ abstract contract KlerosCoreBase is IArbitratorV2 {
237237

238238
sortitionModule.createTree(bytes32(uint256(GENERAL_COURT)), _sortitionExtraData);
239239

240+
uint256[] memory supportedDisputeKits = new uint256[](1);
241+
supportedDisputeKits[0] = DISPUTE_KIT_CLASSIC;
240242
emit CourtCreated(
241-
1,
243+
GENERAL_COURT,
242244
court.parent,
243245
_hiddenVotes,
244246
_courtParameters[0],
245247
_courtParameters[1],
246248
_courtParameters[2],
247249
_courtParameters[3],
248250
_timesPerPeriod,
249-
new uint256[](0)
251+
supportedDisputeKits
250252
);
251253
_enableDisputeKit(GENERAL_COURT, DISPUTE_KIT_CLASSIC, true);
252254
}
@@ -351,7 +353,7 @@ abstract contract KlerosCoreBase is IArbitratorV2 {
351353
if (_supportedDisputeKits[i] == 0 || _supportedDisputeKits[i] >= disputeKits.length) {
352354
revert WrongDisputeKitIndex();
353355
}
354-
court.supportedDisputeKits[_supportedDisputeKits[i]] = true;
356+
_enableDisputeKit(uint96(courtID), _supportedDisputeKits[i], true);
355357
}
356358
// Check that Classic DK support was added.
357359
if (!court.supportedDisputeKits[DISPUTE_KIT_CLASSIC]) revert MustSupportDisputeKitClassic();
@@ -370,7 +372,7 @@ abstract contract KlerosCoreBase is IArbitratorV2 {
370372
// Update the parent.
371373
courts[_parent].children.push(courtID);
372374
emit CourtCreated(
373-
courtID,
375+
uint96(courtID),
374376
_parent,
375377
_hiddenVotes,
376378
_minStake,
@@ -1061,7 +1063,7 @@ abstract contract KlerosCoreBase is IArbitratorV2 {
10611063
bool _alreadyTransferred,
10621064
OnError _onError
10631065
) internal returns (bool) {
1064-
if (_courtID == FORKING_COURT || _courtID > courts.length) {
1066+
if (_courtID == FORKING_COURT || _courtID >= courts.length) {
10651067
_stakingFailed(_onError, StakingResult.CannotStakeInThisCourt); // Staking directly into the forking court is not allowed.
10661068
return false;
10671069
}
@@ -1102,6 +1104,7 @@ abstract contract KlerosCoreBase is IArbitratorV2 {
11021104
if (_result == StakingResult.CannotStakeInMoreCourts) revert StakingInTooManyCourts();
11031105
if (_result == StakingResult.CannotStakeInThisCourt) revert StakingNotPossibeInThisCourt();
11041106
if (_result == StakingResult.CannotStakeLessThanMinStake) revert StakingLessThanCourtMinStake();
1107+
if (_result == StakingResult.CannotStakeZeroWhenNoStake) revert StakingZeroWhenNoStake();
11051108
}
11061109

11071110
/// @dev Gets a court ID, the minimum number of jurors and an ID of a dispute kit from a specified extra data bytes array.
@@ -1147,13 +1150,11 @@ abstract contract KlerosCoreBase is IArbitratorV2 {
11471150
error SortitionModuleOnly();
11481151
error UnsuccessfulCall();
11491152
error InvalidDisputKitParent();
1150-
error DepthLevelMax();
11511153
error MinStakeLowerThanParentCourt();
11521154
error UnsupportedDisputeKit();
11531155
error InvalidForkingCourtAsParent();
11541156
error WrongDisputeKitIndex();
11551157
error CannotDisableClassicDK();
1156-
error ArraysLengthMismatch();
11571158
error StakingInTooManyCourts();
11581159
error StakingNotPossibeInThisCourt();
11591160
error StakingLessThanCourtMinStake();
@@ -1177,4 +1178,5 @@ abstract contract KlerosCoreBase is IArbitratorV2 {
11771178
error TransferFailed();
11781179
error WhenNotPausedOnly();
11791180
error WhenPausedOnly();
1181+
error StakingZeroWhenNoStake();
11801182
}

contracts/src/arbitration/SortitionModuleBase.sol

+6-2
Original file line numberDiff line numberDiff line change
@@ -265,10 +265,14 @@ abstract contract SortitionModuleBase is ISortitionModule {
265265
uint256 currentStake = stakeOf(_account, _courtID);
266266

267267
uint256 nbCourts = juror.courtIDs.length;
268-
if (_newStake == 0 && (nbCourts >= MAX_STAKE_PATHS || currentStake == 0)) {
268+
if (currentStake == 0 && nbCourts >= MAX_STAKE_PATHS) {
269269
return (0, 0, StakingResult.CannotStakeInMoreCourts); // Prevent staking beyond MAX_STAKE_PATHS but unstaking is always allowed.
270270
}
271271

272+
if (currentStake == 0 && _newStake == 0) {
273+
return (0, 0, StakingResult.CannotStakeZeroWhenNoStake); // Forbid staking 0 amount when current stake is 0 to avoid flaky behaviour.
274+
}
275+
272276
if (phase != Phase.staking) {
273277
pnkWithdrawal = _deleteDelayedStake(_courtID, _account);
274278

@@ -296,7 +300,7 @@ abstract contract SortitionModuleBase is ISortitionModule {
296300
pnkDeposit = _increaseStake(juror, _courtID, _newStake, currentStake);
297301
}
298302
} else {
299-
pnkWithdrawal += _decreaseStake(juror, _courtID, _newStake, currentStake);
303+
pnkWithdrawal = _decreaseStake(juror, _courtID, _newStake, currentStake);
300304
}
301305

302306
// Update the sortition sum tree.

contracts/src/arbitration/dispute-kits/DisputeKitClassic.sol

+4-1
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,6 @@ contract DisputeKitClassic is IDisputeKit, Initializable, UUPSProxiable {
238238
(uint96 courtID, , , , ) = core.disputes(_coreDisputeID);
239239
bytes32 key = bytes32(uint256(courtID)); // Get the ID of the tree.
240240

241-
// TODO: Handle the situation when no one has staked yet.
242241
drawnAddress = sortitionModule.draw(key, _coreDisputeID, _nonce);
243242

244243
if (_postDrawCheck(_coreDisputeID, drawnAddress)) {
@@ -603,6 +602,10 @@ contract DisputeKitClassic is IDisputeKit, Initializable, UUPSProxiable {
603602
/// @param _coreDisputeID ID of the dispute in the core contract.
604603
/// @param _juror Chosen address.
605604
/// @return Whether the address can be drawn or not.
605+
/// Note that we don't check the minStake requirement here because of the implicit staking in parent courts.
606+
/// minStake is checked directly during staking process however it's possible for the juror to get drawn
607+
/// while having < minStake if it is later increased by governance.
608+
/// This issue is expected and harmless since we check for insolvency anyway.
606609
function _postDrawCheck(uint256 _coreDisputeID, address _juror) internal view returns (bool) {
607610
(uint96 courtID, , , , ) = core.disputes(_coreDisputeID);
608611
uint256 lockedAmountPerJuror = core

contracts/src/arbitration/dispute-kits/DisputeKitSybilResistant.sol

+4-1
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,6 @@ contract DisputeKitSybilResistant is IDisputeKit, Initializable, UUPSProxiable {
255255
(uint96 courtID, , , , ) = core.disputes(_coreDisputeID);
256256
bytes32 key = bytes32(uint256(courtID)); // Get the ID of the tree.
257257

258-
// TODO: Handle the situation when no one has staked yet.
259258
drawnAddress = sortitionModule.draw(key, _coreDisputeID, _nonce);
260259

261260
if (_postDrawCheck(_coreDisputeID, drawnAddress) && !round.alreadyDrawn[drawnAddress]) {
@@ -621,6 +620,10 @@ contract DisputeKitSybilResistant is IDisputeKit, Initializable, UUPSProxiable {
621620
/// @param _coreDisputeID ID of the dispute in the core contract.
622621
/// @param _juror Chosen address.
623622
/// @return Whether the address can be drawn or not.
623+
/// Note that we don't check the minStake requirement here because of the implicit staking in parent courts.
624+
/// minStake is checked directly during staking process however it's possible for the juror to get drawn
625+
/// while having < minStake if it is later increased by governance.
626+
/// This issue is expected and harmless since we check for insolvency anyway.
624627
function _postDrawCheck(uint256 _coreDisputeID, address _juror) internal view returns (bool) {
625628
(uint96 courtID, , , , ) = core.disputes(_coreDisputeID);
626629
uint256 lockedAmountPerJuror = core

contracts/src/arbitration/interfaces/IDisputeKit.sol

+1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ interface IDisputeKit {
4141
/// @param _coreDisputeID The ID of the dispute in Kleros Core, not in the Dispute Kit.
4242
/// @param _numberOfChoices Number of choices of the dispute
4343
/// @param _extraData Additional info about the dispute, for possible use in future dispute kits.
44+
/// @param _nbVotes Maximal number of votes this dispute can get. DEPRECATED as we don't need to pass it now. KC handles the count.
4445
function createDispute(
4546
uint256 _coreDisputeID,
4647
uint256 _numberOfChoices,

contracts/src/libraries/Constants.sol

+3-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ uint96 constant FORKING_COURT = 0; // Index of the forking court.
99
uint96 constant GENERAL_COURT = 1; // Index of the default (general) court.
1010

1111
// Dispute Kits
12-
uint256 constant NULL_DISPUTE_KIT = 0; // Null pattern to indicate a top-level DK which has no parent.
12+
uint256 constant NULL_DISPUTE_KIT = 0; // Null pattern to indicate a top-level DK which has no parent. DEPRECATED, as its main purpose was to accommodate forest structure which is not used now.
1313
uint256 constant DISPUTE_KIT_CLASSIC = 1; // Index of the default DK. 0 index is skipped.
1414

1515
// Sortition Module
@@ -33,5 +33,6 @@ enum StakingResult {
3333
CannotStakeInThisCourt,
3434
CannotStakeLessThanMinStake,
3535
CannotStakeMoreThanMaxStakePerJuror,
36-
CannotStakeMoreThanMaxTotalStaked
36+
CannotStakeMoreThanMaxTotalStaked,
37+
CannotStakeZeroWhenNoStake
3738
}

contracts/src/test/KlerosCoreMock.sol

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
/// @custom:authors: [@unknownunknown1]
4+
/// @custom:reviewers: []
5+
/// @custom:auditors: []
6+
/// @custom:bounties: []
7+
/// @custom:deployments: []
8+
9+
pragma solidity 0.8.24;
10+
11+
import "../arbitration/KlerosCore.sol";
12+
13+
/// @title KlerosCoreMock
14+
/// KlerosCore with view functions to use in Foundry tests.
15+
contract KlerosCoreMock is KlerosCore {
16+
function getCourtChildren(uint256 _courtId) external view returns (uint256[] memory children) {
17+
children = courts[_courtId].children;
18+
}
19+
20+
function extraDataToCourtIDMinJurorsDisputeKit(
21+
bytes memory _extraData
22+
) external view returns (uint96 courtID, uint256 minJurors, uint256 disputeKitID) {
23+
(courtID, minJurors, disputeKitID) = _extraDataToCourtIDMinJurorsDisputeKit(_extraData);
24+
}
25+
}
+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
/**
4+
* @custom:authors: [unknownunknown1]
5+
* @custom:reviewers: []
6+
* @custom:auditors: []
7+
* @custom:bounties: []
8+
* @custom:deployments: []
9+
*/
10+
11+
pragma solidity 0.8.24;
12+
13+
import "../arbitration/SortitionModule.sol";
14+
15+
/// @title SortitionModuleMock
16+
/// @dev Adds getter functions to sortition module for Foundry tests.
17+
contract SortitionModuleMock is SortitionModule {
18+
function getSortitionProperties(bytes32 _key) external view returns (uint256 K, uint256 nodeLength) {
19+
SortitionSumTree storage tree = sortitionSumTrees[_key];
20+
K = tree.K;
21+
nodeLength = tree.nodes.length;
22+
}
23+
}

contracts/test/arbitration/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ describe("DisputeKitClassic", async () => {
3030
expect(events2[0].args._feeForJuror).to.equal(ethers.parseUnits("0.1", 18));
3131
expect(events2[0].args._jurorsForCourtJump).to.equal(256);
3232
expect(events2[0].args._timesPerPeriod).to.deep.equal([0, 0, 0, 10]);
33-
expect(events2[0].args._supportedDisputeKits).to.deep.equal([]);
33+
expect(events2[0].args._supportedDisputeKits).to.deep.equal([1]);
3434

3535
const events3 = await core.queryFilter(core.filters.DisputeKitEnabled());
3636
expect(events3.length).to.equal(1);

contracts/test/foundry/Contract.t.sol

Whitespace-only changes.

0 commit comments

Comments
 (0)