From 3a946ee4c0fff7bc8df392e9392cbd30db63d96c Mon Sep 17 00:00:00 2001 From: jaybuidl Date: Wed, 19 Jun 2024 11:39:00 +0100 Subject: [PATCH 1/4] refactor: no coupling between dispute kit and sortition (WIP) --- contracts/src/arbitration/KlerosCoreBase.sol | 20 ++++++++++++++++++- .../dispute-kits/DisputeKitClassic.sol | 18 ++++------------- .../dispute-kits/DisputeKitSybilResistant.sol | 18 ++++------------- .../arbitration/interfaces/IDisputeKit.sol | 2 +- 4 files changed, 28 insertions(+), 30 deletions(-) diff --git a/contracts/src/arbitration/KlerosCoreBase.sol b/contracts/src/arbitration/KlerosCoreBase.sol index ce6ed6202..a187977ca 100644 --- a/contracts/src/arbitration/KlerosCoreBase.sol +++ b/contracts/src/arbitration/KlerosCoreBase.sol @@ -603,7 +603,7 @@ abstract contract KlerosCoreBase is IArbitratorV2 { uint256 startIndex = round.drawIterations; // for gas: less storage reads uint256 i; while (i < _iterations && round.drawnJurors.length < round.nbVotes) { - address drawnAddress = disputeKit.draw(_disputeID, startIndex + i++); + address drawnAddress = disputeKit.drawOne(_disputeID, startIndex + i++); if (drawnAddress == address(0)) { continue; } @@ -617,6 +617,16 @@ abstract contract KlerosCoreBase is IArbitratorV2 { round.drawIterations += i; } + function drawOne(uint256 _disputeID, uint256 _nonce) public view returns (address drawnAddress) { + Dispute storage dispute = disputes[_disputeID]; + Round storage round = dispute.rounds[dispute.rounds.length - 1]; + if (msg.sender != address(disputeKits[round.disputeKitID])) revert DisputeKitOnly(); + + bytes32 key = bytes32(uint256(dispute.courtID)); // Get the ID of the tree. + + drawnAddress = sortitionModule.draw(key, _disputeID, _nonce); + } + /// @dev Appeals the ruling of a specified dispute. /// Note: Access restricted to the Dispute Kit for this `disputeID`. /// @param _disputeID The ID of the dispute. @@ -1034,6 +1044,14 @@ abstract contract KlerosCoreBase is IArbitratorV2 { return (_amountInEth * 10 ** currencyRates[_toToken].rateDecimals) / currencyRates[_toToken].rateInEth; } + function isJurorSolvent(uint256 _disputeID, address _account) external view returns (bool) { + Dispute storage dispute = disputes[_disputeID]; + Round storage round = dispute.rounds[dispute.rounds.length - 1]; + uint256 lockedAmountPerJuror = round.pnkAtStakePerJuror; + (uint256 totalStaked, uint256 totalLocked, , ) = sortitionModule.getJurorBalance(_account, dispute.courtID); + return totalStaked >= totalLocked + lockedAmountPerJuror; + } + // ************************************* // // * Internal * // // ************************************* // diff --git a/contracts/src/arbitration/dispute-kits/DisputeKitClassic.sol b/contracts/src/arbitration/dispute-kits/DisputeKitClassic.sol index 0aa857091..9a55f14c2 100644 --- a/contracts/src/arbitration/dispute-kits/DisputeKitClassic.sol +++ b/contracts/src/arbitration/dispute-kits/DisputeKitClassic.sol @@ -222,24 +222,19 @@ contract DisputeKitClassic is IDisputeKit, Initializable, UUPSProxiable { emit DisputeCreation(_coreDisputeID, _numberOfChoices, _extraData); } - /// @dev Draws the juror from the sortition tree. The drawn address is picked up by Kleros Core. + /// @dev Draws one juror. The drawn address is picked up by Kleros Core. /// Note: Access restricted to Kleros Core only. /// @param _coreDisputeID The ID of the dispute in Kleros Core. /// @param _nonce Nonce of the drawing iteration. /// @return drawnAddress The drawn address. - function draw( + function drawOne( uint256 _coreDisputeID, uint256 _nonce ) external override onlyByCore notJumped(_coreDisputeID) returns (address drawnAddress) { Dispute storage dispute = disputes[coreDisputeIDToLocal[_coreDisputeID]]; Round storage round = dispute.rounds[dispute.rounds.length - 1]; - ISortitionModule sortitionModule = core.sortitionModule(); - (uint96 courtID, , , , ) = core.disputes(_coreDisputeID); - bytes32 key = bytes32(uint256(courtID)); // Get the ID of the tree. - - // TODO: Handle the situation when no one has staked yet. - drawnAddress = sortitionModule.draw(key, _coreDisputeID, _nonce); + drawnAddress = core.drawOne(_coreDisputeID, _nonce); if (_postDrawCheck(_coreDisputeID, drawnAddress)) { round.votes.push(Vote({account: drawnAddress, commit: bytes32(0), choice: 0, voted: false})); @@ -604,11 +599,6 @@ contract DisputeKitClassic is IDisputeKit, Initializable, UUPSProxiable { /// @param _juror Chosen address. /// @return Whether the address can be drawn or not. function _postDrawCheck(uint256 _coreDisputeID, address _juror) internal view returns (bool) { - (uint96 courtID, , , , ) = core.disputes(_coreDisputeID); - uint256 lockedAmountPerJuror = core - .getRoundInfo(_coreDisputeID, core.getNumberOfRounds(_coreDisputeID) - 1) - .pnkAtStakePerJuror; - (uint256 totalStaked, uint256 totalLocked, , ) = core.sortitionModule().getJurorBalance(_juror, courtID); - return totalStaked >= totalLocked + lockedAmountPerJuror; + return core.isJurorSolvent(_coreDisputeID, _juror); } } diff --git a/contracts/src/arbitration/dispute-kits/DisputeKitSybilResistant.sol b/contracts/src/arbitration/dispute-kits/DisputeKitSybilResistant.sol index 40b805c05..f35b4b009 100644 --- a/contracts/src/arbitration/dispute-kits/DisputeKitSybilResistant.sol +++ b/contracts/src/arbitration/dispute-kits/DisputeKitSybilResistant.sol @@ -239,24 +239,19 @@ contract DisputeKitSybilResistant is IDisputeKit, Initializable, UUPSProxiable { emit DisputeCreation(_coreDisputeID, _numberOfChoices, _extraData); } - /// @dev Draws the juror from the sortition tree. The drawn address is picked up by Kleros Core. + /// @dev Draws one juror. The drawn address is picked up by Kleros Core. /// Note: Access restricted to Kleros Core only. /// @param _coreDisputeID The ID of the dispute in Kleros Core. /// @param _nonce Nonce of the drawing iteration. /// @return drawnAddress The drawn address. - function draw( + function drawOne( uint256 _coreDisputeID, uint256 _nonce ) external override onlyByCore notJumped(_coreDisputeID) returns (address drawnAddress) { Dispute storage dispute = disputes[coreDisputeIDToLocal[_coreDisputeID]]; Round storage round = dispute.rounds[dispute.rounds.length - 1]; - ISortitionModule sortitionModule = core.sortitionModule(); - (uint96 courtID, , , , ) = core.disputes(_coreDisputeID); - bytes32 key = bytes32(uint256(courtID)); // Get the ID of the tree. - - // TODO: Handle the situation when no one has staked yet. - drawnAddress = sortitionModule.draw(key, _coreDisputeID, _nonce); + drawnAddress = core.drawOne(_coreDisputeID, _nonce); if (_postDrawCheck(_coreDisputeID, drawnAddress) && !round.alreadyDrawn[drawnAddress]) { round.votes.push(Vote({account: drawnAddress, commit: bytes32(0), choice: 0, voted: false})); @@ -622,12 +617,7 @@ contract DisputeKitSybilResistant is IDisputeKit, Initializable, UUPSProxiable { /// @param _juror Chosen address. /// @return Whether the address can be drawn or not. function _postDrawCheck(uint256 _coreDisputeID, address _juror) internal view returns (bool) { - (uint96 courtID, , , , ) = core.disputes(_coreDisputeID); - uint256 lockedAmountPerJuror = core - .getRoundInfo(_coreDisputeID, core.getNumberOfRounds(_coreDisputeID) - 1) - .pnkAtStakePerJuror; - (uint256 totalStaked, uint256 totalLocked, , ) = core.sortitionModule().getJurorBalance(_juror, courtID); - if (totalStaked < totalLocked + lockedAmountPerJuror) { + if (core.isJurorSolvent(_coreDisputeID, _juror)) { return false; } else { return _proofOfHumanity(_juror); diff --git a/contracts/src/arbitration/interfaces/IDisputeKit.sol b/contracts/src/arbitration/interfaces/IDisputeKit.sol index 86430f663..305e57d58 100644 --- a/contracts/src/arbitration/interfaces/IDisputeKit.sol +++ b/contracts/src/arbitration/interfaces/IDisputeKit.sol @@ -53,7 +53,7 @@ interface IDisputeKit { /// @param _coreDisputeID The ID of the dispute in Kleros Core, not in the Dispute Kit. /// @param _nonce Nonce. /// @return drawnAddress The drawn address. - function draw(uint256 _coreDisputeID, uint256 _nonce) external returns (address drawnAddress); + function drawOne(uint256 _coreDisputeID, uint256 _nonce) external returns (address drawnAddress); // ************************************* // // * Public Views * // From 71aa53f0835385e7b062ec3ff03d79f055e8d793 Mon Sep 17 00:00:00 2001 From: unknownunknown1 Date: Wed, 19 Jun 2024 21:35:07 +1000 Subject: [PATCH 2/4] fix(KC): reflect changes in CoreUniversity --- .../university/KlerosCoreUniversity.sol | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/contracts/src/arbitration/university/KlerosCoreUniversity.sol b/contracts/src/arbitration/university/KlerosCoreUniversity.sol index d3e5be37c..8f8e21136 100644 --- a/contracts/src/arbitration/university/KlerosCoreUniversity.sol +++ b/contracts/src/arbitration/university/KlerosCoreUniversity.sol @@ -592,7 +592,7 @@ contract KlerosCoreUniversity is IArbitratorV2, UUPSProxiable, Initializable { { IDisputeKit disputeKit = disputeKits[round.disputeKitID]; uint256 iteration = round.drawIterations + 1; - address drawnAddress = disputeKit.draw(_disputeID, iteration); + address drawnAddress = disputeKit.drawOne(_disputeID, iteration); if (drawnAddress == address(0)) { revert NoJurorDrawn(); } @@ -607,6 +607,16 @@ contract KlerosCoreUniversity is IArbitratorV2, UUPSProxiable, Initializable { sortitionModule.setTransientJuror(address(0)); } + function drawOne(uint256 _disputeID, uint256 _nonce) public view returns (address drawnAddress) { + Dispute storage dispute = disputes[_disputeID]; + Round storage round = dispute.rounds[dispute.rounds.length - 1]; + if (msg.sender != address(disputeKits[round.disputeKitID])) revert DisputeKitOnly(); + + bytes32 key = bytes32(uint256(dispute.courtID)); // Get the ID of the tree. + + drawnAddress = sortitionModule.draw(key, _disputeID, _nonce); + } + /// @dev Appeals the ruling of a specified dispute. /// Note: Access restricted to the Dispute Kit for this `disputeID`. /// @param _disputeID The ID of the dispute. @@ -1023,6 +1033,14 @@ contract KlerosCoreUniversity is IArbitratorV2, UUPSProxiable, Initializable { return (_amountInEth * 10 ** currencyRates[_toToken].rateDecimals) / currencyRates[_toToken].rateInEth; } + function isJurorSolvent(uint256 _disputeID, address _account) external view returns (bool) { + Dispute storage dispute = disputes[_disputeID]; + Round storage round = dispute.rounds[dispute.rounds.length - 1]; + uint256 lockedAmountPerJuror = round.pnkAtStakePerJuror; + (uint256 totalStaked, uint256 totalLocked, , ) = sortitionModule.getJurorBalance(_account, dispute.courtID); + return totalStaked >= totalLocked + lockedAmountPerJuror; + } + // ************************************* // // * Internal * // // ************************************* // From d74e019bb1ad7998628ad02687f6921d1d059dfd Mon Sep 17 00:00:00 2001 From: unknownunknown1 Date: Wed, 19 Jun 2024 21:38:29 +1000 Subject: [PATCH 3/4] docs(DK): comment change --- contracts/src/arbitration/interfaces/IDisputeKit.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/src/arbitration/interfaces/IDisputeKit.sol b/contracts/src/arbitration/interfaces/IDisputeKit.sol index 305e57d58..0f73f30c2 100644 --- a/contracts/src/arbitration/interfaces/IDisputeKit.sol +++ b/contracts/src/arbitration/interfaces/IDisputeKit.sol @@ -48,7 +48,7 @@ interface IDisputeKit { uint256 _nbVotes ) external; - /// @dev Draws the juror from the sortition tree. The drawn address is picked up by Kleros Core. + /// @dev Draws one juror. The drawn address is picked up by Kleros Core. /// Note: Access restricted to Kleros Core only. /// @param _coreDisputeID The ID of the dispute in Kleros Core, not in the Dispute Kit. /// @param _nonce Nonce. From c3d656c6f27eb08e36ee594390f786483c852d58 Mon Sep 17 00:00:00 2001 From: unknownunknown1 Date: Wed, 19 Jun 2024 21:44:19 +1000 Subject: [PATCH 4/4] fix(DK): post draw check fix --- .../arbitration/dispute-kits/DisputeKitSybilResistant.sol | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/contracts/src/arbitration/dispute-kits/DisputeKitSybilResistant.sol b/contracts/src/arbitration/dispute-kits/DisputeKitSybilResistant.sol index f35b4b009..d99b60b28 100644 --- a/contracts/src/arbitration/dispute-kits/DisputeKitSybilResistant.sol +++ b/contracts/src/arbitration/dispute-kits/DisputeKitSybilResistant.sol @@ -617,11 +617,7 @@ contract DisputeKitSybilResistant is IDisputeKit, Initializable, UUPSProxiable { /// @param _juror Chosen address. /// @return Whether the address can be drawn or not. function _postDrawCheck(uint256 _coreDisputeID, address _juror) internal view returns (bool) { - if (core.isJurorSolvent(_coreDisputeID, _juror)) { - return false; - } else { - return _proofOfHumanity(_juror); - } + return core.isJurorSolvent(_coreDisputeID, _juror) && _proofOfHumanity(_juror); } /// @dev Checks if an address belongs to the Proof of Humanity registry.