diff --git a/contracts/CHANGELOG.md b/contracts/CHANGELOG.md index 14e0ff78c..e6db23764 100644 --- a/contracts/CHANGELOG.md +++ b/contracts/CHANGELOG.md @@ -16,6 +16,7 @@ The format is based on [Common Changelog](https://common-changelog.org/). - **Breaking:** Apply the penalties to the stakes in the Sortition Tree ([#2107](https://github.com/kleros/kleros-v2/issues/2107)) - **Breaking:** Make `SortitionModule.getJurorBalance().stakedInCourt` include the penalties ([#2107](https://github.com/kleros/kleros-v2/issues/2107)) - **Breaking:** Add a new field `drawnJurorFromCourtIDs` to the `Round` struct in `KlerosCoreBase` and `KlerosCoreUniversity` ([#2107](https://github.com/kleros/kleros-v2/issues/2107)) +- **Breaking:** Add a new state variable `jumpDisputeKitID` to the `DisputeKitClassicBase` contract ([#2114](https://github.com/kleros/kleros-v2/issues/2114)) - Make `IDisputeKit.draw()` and `ISortitionModule.draw()` return the court ID from which the juror was drawn ([#2107](https://github.com/kleros/kleros-v2/issues/2107)) - Rename `SortitionModule.setJurorInactive()` to `SortitionModule.forcedUnstakeAllCourts()` ([#2107](https://github.com/kleros/kleros-v2/issues/2107)) - Allow stake changes to by-pass delayed stakes when initiated by the SortitionModule by setting the `_noDelay` parameter to `true` in `SortitionModule.validateStake()` ([#2107](https://github.com/kleros/kleros-v2/issues/2107)) @@ -35,6 +36,7 @@ The format is based on [Common Changelog](https://common-changelog.org/). ### Added - Allow the dispute kits to force an early court jump and to override the number of votes after an appeal (future-proofing) ([#2110](https://github.com/kleros/kleros-v2/issues/2110)) +- Allow the dispute kits to specify which new dispute kit to use when a court jump occurs ([#2114](https://github.com/kleros/kleros-v2/issues/2114)) ### Fixed diff --git a/contracts/deploy/00-home-chain-arbitration-neo.ts b/contracts/deploy/00-home-chain-arbitration-neo.ts index f5211c4af..36a895c74 100644 --- a/contracts/deploy/00-home-chain-arbitration-neo.ts +++ b/contracts/deploy/00-home-chain-arbitration-neo.ts @@ -28,10 +28,11 @@ const deployArbitration: DeployFunction = async (hre: HardhatRuntimeEnvironment) await deployUpgradable(deployments, "EvidenceModule", { from: deployer, args: [deployer], log: true }); + const classicDisputeKitID = 1; // Classic DK const disputeKit = await deployUpgradable(deployments, "DisputeKitClassicNeo", { from: deployer, contract: "DisputeKitClassic", - args: [deployer, ZeroAddress, weth.target], + args: [deployer, ZeroAddress, weth.target, classicDisputeKitID], log: true, }); @@ -124,24 +125,27 @@ const deployArbitration: DeployFunction = async (hre: HardhatRuntimeEnvironment) // Extra dispute kits const disputeKitShutter = await deployUpgradable(deployments, "DisputeKitShutter", { from: deployer, - args: [deployer, core.target, weth.target], + args: [deployer, core.target, weth.target, classicDisputeKitID], log: true, }); await core.addNewDisputeKit(disputeKitShutter.address); + const disputeKitShutterID = (await core.getDisputeKitsLength()) - 1n; const disputeKitGated = await deployUpgradable(deployments, "DisputeKitGated", { from: deployer, - args: [deployer, core.target, weth.target], + args: [deployer, core.target, weth.target, classicDisputeKitID], log: true, }); await core.addNewDisputeKit(disputeKitGated.address); + const disputeKitGatedID = (await core.getDisputeKitsLength()) - 1n; const disputeKitGatedShutter = await deployUpgradable(deployments, "DisputeKitGatedShutter", { from: deployer, - args: [deployer, core.target, weth.target], + args: [deployer, core.target, weth.target, disputeKitShutterID], // Does not jump to DKClassic log: true, }); await core.addNewDisputeKit(disputeKitGatedShutter.address); + const disputeKitGatedShutterID = (await core.getDisputeKitsLength()) - 1n; // Snapshot proxy await deploy("KlerosCoreSnapshotProxy", { diff --git a/contracts/deploy/00-home-chain-arbitration.ts b/contracts/deploy/00-home-chain-arbitration.ts index 1d590813f..25291052d 100644 --- a/contracts/deploy/00-home-chain-arbitration.ts +++ b/contracts/deploy/00-home-chain-arbitration.ts @@ -34,9 +34,10 @@ const deployArbitration: DeployFunction = async (hre: HardhatRuntimeEnvironment) log: true, }); + const classicDisputeKitID = 1; // Classic DK const disputeKit = await deployUpgradable(deployments, "DisputeKitClassic", { from: deployer, - args: [deployer, ZeroAddress, weth.target], + args: [deployer, ZeroAddress, weth.target, classicDisputeKitID], log: true, }); @@ -105,27 +106,30 @@ const deployArbitration: DeployFunction = async (hre: HardhatRuntimeEnvironment) // Extra dispute kits const disputeKitShutter = await deployUpgradable(deployments, "DisputeKitShutter", { from: deployer, - args: [deployer, core.target, weth.target], + args: [deployer, core.target, weth.target, classicDisputeKitID], log: true, }); await core.addNewDisputeKit(disputeKitShutter.address); - await core.enableDisputeKits(Courts.GENERAL, [2], true); // enable disputeKitShutter on the General Court + const disputeKitShutterID = (await core.getDisputeKitsLength()) - 1n; + await core.enableDisputeKits(Courts.GENERAL, [disputeKitShutterID], true); // enable disputeKitShutter on the General Court const disputeKitGated = await deployUpgradable(deployments, "DisputeKitGated", { from: deployer, - args: [deployer, core.target, weth.target], + args: [deployer, core.target, weth.target, classicDisputeKitID], log: true, }); await core.addNewDisputeKit(disputeKitGated.address); - await core.enableDisputeKits(Courts.GENERAL, [3], true); // enable disputeKitGated on the General Court + const disputeKitGatedID = (await core.getDisputeKitsLength()) - 1n; + await core.enableDisputeKits(Courts.GENERAL, [disputeKitGatedID], true); // enable disputeKitGated on the General Court const disputeKitGatedShutter = await deployUpgradable(deployments, "DisputeKitGatedShutter", { from: deployer, - args: [deployer, core.target, weth.target], + args: [deployer, core.target, weth.target, disputeKitShutterID], // Does not jump to DKClassic log: true, }); await core.addNewDisputeKit(disputeKitGatedShutter.address); - await core.enableDisputeKits(Courts.GENERAL, [4], true); // enable disputeKitGatedShutter on the General Court + const disputeKitGatedShutterID = (await core.getDisputeKitsLength()) - 1n; + await core.enableDisputeKits(Courts.GENERAL, [disputeKitGatedShutterID], true); // enable disputeKitGatedShutter on the General Court // Snapshot proxy await deploy("KlerosCoreSnapshotProxy", { diff --git a/contracts/src/arbitration/KlerosCoreBase.sol b/contracts/src/arbitration/KlerosCoreBase.sol index a76930051..fc8095715 100644 --- a/contracts/src/arbitration/KlerosCoreBase.sol +++ b/contracts/src/arbitration/KlerosCoreBase.sol @@ -1091,8 +1091,12 @@ abstract contract KlerosCoreBase is IArbitratorV2, Initializable, UUPSProxiable courtJump = true; if (!courts[newCourtID].supportedDisputeKits[newDisputeKitID]) { - // Switch to classic dispute kit if parent court doesn't support the current one. - newDisputeKitID = DISPUTE_KIT_CLASSIC; + // The current Dispute Kit is not compatible with the new court, jump to another Dispute Kit. + newDisputeKitID = disputeKits[_round.disputeKitID].getJumpDisputeKitID(); + if (newDisputeKitID == NULL_DISPUTE_KIT || !courts[newCourtID].supportedDisputeKits[newDisputeKitID]) { + // The new Dispute Kit is not defined or still not compatible, fall back to `DisputeKitClassic` which is always supported. + newDisputeKitID = DISPUTE_KIT_CLASSIC; + } disputeKitJump = true; } } diff --git a/contracts/src/arbitration/dispute-kits/DisputeKitClassic.sol b/contracts/src/arbitration/dispute-kits/DisputeKitClassic.sol index ce5932c92..af89d2816 100644 --- a/contracts/src/arbitration/dispute-kits/DisputeKitClassic.sol +++ b/contracts/src/arbitration/dispute-kits/DisputeKitClassic.sol @@ -11,7 +11,7 @@ import {DisputeKitClassicBase, KlerosCore} from "./DisputeKitClassicBase.sol"; /// - an incentive system: equal split between coherent votes, /// - an appeal system: fund 2 choices only, vote on any choice. contract DisputeKitClassic is DisputeKitClassicBase { - string public constant override version = "0.12.0"; + string public constant override version = "0.13.0"; // ************************************* // // * Constructor * // @@ -26,12 +26,18 @@ contract DisputeKitClassic is DisputeKitClassicBase { /// @param _owner The owner's address. /// @param _core The KlerosCore arbitrator. /// @param _wNative The wrapped native token address, typically wETH. - function initialize(address _owner, KlerosCore _core, address _wNative) external reinitializer(1) { - __DisputeKitClassicBase_initialize(_owner, _core, _wNative); + /// @param _jumpDisputeKitID The ID of the dispute kit to switch to after the court jump. + function initialize( + address _owner, + KlerosCore _core, + address _wNative, + uint256 _jumpDisputeKitID + ) external reinitializer(1) { + __DisputeKitClassicBase_initialize(_owner, _core, _wNative, _jumpDisputeKitID); } - function reinitialize(address _wNative) external reinitializer(9) { - wNative = _wNative; + function reinitialize(uint256 _jumpDisputeKitID) external reinitializer(10) { + jumpDisputeKitID = _jumpDisputeKitID; } // ************************ // diff --git a/contracts/src/arbitration/dispute-kits/DisputeKitClassicBase.sol b/contracts/src/arbitration/dispute-kits/DisputeKitClassicBase.sol index 6fca9b02b..577364cde 100644 --- a/contracts/src/arbitration/dispute-kits/DisputeKitClassicBase.sol +++ b/contracts/src/arbitration/dispute-kits/DisputeKitClassicBase.sol @@ -6,7 +6,7 @@ import {KlerosCore, KlerosCoreBase, IDisputeKit, ISortitionModule} from "../Kler import {Initializable} from "../../proxy/Initializable.sol"; import {UUPSProxiable} from "../../proxy/UUPSProxiable.sol"; import {SafeSend} from "../../libraries/SafeSend.sol"; -import {ONE_BASIS_POINT} from "../../libraries/Constants.sol"; +import {ONE_BASIS_POINT, DISPUTE_KIT_CLASSIC} from "../../libraries/Constants.sol"; /// @title DisputeKitClassicBase /// Abstract Dispute kit classic implementation of the Kleros v1 features including: @@ -68,6 +68,7 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi public alreadyDrawn; // True if the address has already been drawn, false by default. To be added to the Round struct when fully redeploying rather than upgrading. mapping(uint256 coreDisputeID => bool) public coreDisputeIDToActive; // True if this dispute kit is active for this core dispute ID. address public wNative; // The wrapped native token for safeSend(). + uint256 public jumpDisputeKitID; // The ID of the dispute kit in Kleros Core disputeKits array that the dispute should switch to after the court jump, in case the new court doesn't support this dispute kit. // ************************************* // // * Events * // @@ -147,14 +148,17 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi /// @param _owner The owner's address. /// @param _core The KlerosCore arbitrator. /// @param _wNative The wrapped native token address, typically wETH. + /// @param _jumpDisputeKitID The ID of the dispute kit to switch to after the court jump. function __DisputeKitClassicBase_initialize( address _owner, KlerosCore _core, - address _wNative + address _wNative, + uint256 _jumpDisputeKitID ) internal onlyInitializing { owner = _owner; core = _core; wNative = _wNative; + jumpDisputeKitID = _jumpDisputeKitID; } // ************************ // @@ -182,6 +186,12 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi core = KlerosCore(_core); } + /// @dev Changes the dispute kit ID used for the jump. + /// @param _jumpDisputeKitID The new value for the `jumpDisputeKitID` storage variable. + function changeJumpDisputeKitID(uint256 _jumpDisputeKitID) external onlyByOwner { + jumpDisputeKitID = _jumpDisputeKitID; + } + // ************************************* // // * State Modifiers * // // ************************************* // @@ -637,6 +647,13 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi return (_currentNbVotes * 2) + 1; } + /// @dev Returns the dispute kid ID be used after court jump by Kleros Core. + /// @return The ID of the dispute kit in Kleros Core disputeKits array. + function getJumpDisputeKitID() external view override returns (uint256) { + // Fall back to classic DK in case the jump ID is not defined. + return jumpDisputeKitID == 0 ? DISPUTE_KIT_CLASSIC : jumpDisputeKitID; + } + /// @dev Returns true if the specified voter was active in this round. /// @param _coreDisputeID The ID of the dispute in Kleros Core, not in the Dispute Kit. /// @param _coreRoundID The ID of the round in Kleros Core, not in the Dispute Kit. diff --git a/contracts/src/arbitration/dispute-kits/DisputeKitGated.sol b/contracts/src/arbitration/dispute-kits/DisputeKitGated.sol index c0e70f741..9097a8fd3 100644 --- a/contracts/src/arbitration/dispute-kits/DisputeKitGated.sol +++ b/contracts/src/arbitration/dispute-kits/DisputeKitGated.sol @@ -27,7 +27,7 @@ interface IBalanceHolderERC1155 { /// - an incentive system: equal split between coherent votes, /// - an appeal system: fund 2 choices only, vote on any choice. contract DisputeKitGated is DisputeKitClassicBase { - string public constant override version = "0.12.0"; + string public constant override version = "0.13.0"; // ************************************* // // * Constructor * // @@ -42,12 +42,18 @@ contract DisputeKitGated is DisputeKitClassicBase { /// @param _owner The owner's address. /// @param _core The KlerosCore arbitrator. /// @param _wNative The wrapped native token address, typically wETH. - function initialize(address _owner, KlerosCore _core, address _wNative) external reinitializer(1) { - __DisputeKitClassicBase_initialize(_owner, _core, _wNative); + /// @param _jumpDisputeKitID The ID of the dispute kit to switch to after the court jump. + function initialize( + address _owner, + KlerosCore _core, + address _wNative, + uint256 _jumpDisputeKitID + ) external reinitializer(1) { + __DisputeKitClassicBase_initialize(_owner, _core, _wNative, _jumpDisputeKitID); } - function reinitialize(address _wNative) external reinitializer(9) { - wNative = _wNative; + function reinitialize(uint256 _jumpDisputeKitID) external reinitializer(10) { + jumpDisputeKitID = _jumpDisputeKitID; } // ************************ // diff --git a/contracts/src/arbitration/dispute-kits/DisputeKitGatedShutter.sol b/contracts/src/arbitration/dispute-kits/DisputeKitGatedShutter.sol index c5de2483b..158bfc5a6 100644 --- a/contracts/src/arbitration/dispute-kits/DisputeKitGatedShutter.sol +++ b/contracts/src/arbitration/dispute-kits/DisputeKitGatedShutter.sol @@ -28,7 +28,7 @@ interface IBalanceHolderERC1155 { /// - an incentive system: equal split between coherent votes, /// - an appeal system: fund 2 choices only, vote on any choice. contract DisputeKitGatedShutter is DisputeKitClassicBase { - string public constant override version = "0.12.0"; + string public constant override version = "0.13.0"; // ************************************* // // * Events * // @@ -61,12 +61,18 @@ contract DisputeKitGatedShutter is DisputeKitClassicBase { /// @param _owner The owner's address. /// @param _core The KlerosCore arbitrator. /// @param _wNative The wrapped native token address, typically wETH. - function initialize(address _owner, KlerosCore _core, address _wNative) external reinitializer(1) { - __DisputeKitClassicBase_initialize(_owner, _core, _wNative); + /// @param _jumpDisputeKitID The ID of the dispute kit to switch to after the court jump. + function initialize( + address _owner, + KlerosCore _core, + address _wNative, + uint256 _jumpDisputeKitID + ) external reinitializer(1) { + __DisputeKitClassicBase_initialize(_owner, _core, _wNative, _jumpDisputeKitID); } - function reinitialize(address _wNative) external reinitializer(9) { - wNative = _wNative; + function reinitialize(uint256 _jumpDisputeKitID) external reinitializer(10) { + jumpDisputeKitID = _jumpDisputeKitID; } // ************************ // diff --git a/contracts/src/arbitration/dispute-kits/DisputeKitShutter.sol b/contracts/src/arbitration/dispute-kits/DisputeKitShutter.sol index 886f5dbec..0dfbf6985 100644 --- a/contracts/src/arbitration/dispute-kits/DisputeKitShutter.sol +++ b/contracts/src/arbitration/dispute-kits/DisputeKitShutter.sol @@ -12,7 +12,7 @@ import {DisputeKitClassicBase, KlerosCore} from "./DisputeKitClassicBase.sol"; /// - an incentive system: equal split between coherent votes, /// - an appeal system: fund 2 choices only, vote on any choice. contract DisputeKitShutter is DisputeKitClassicBase { - string public constant override version = "0.12.0"; + string public constant override version = "0.13.0"; // ************************************* // // * Events * // @@ -45,12 +45,18 @@ contract DisputeKitShutter is DisputeKitClassicBase { /// @param _owner The owner's address. /// @param _core The KlerosCore arbitrator. /// @param _wNative The wrapped native token address, typically wETH. - function initialize(address _owner, KlerosCore _core, address _wNative) external reinitializer(1) { - __DisputeKitClassicBase_initialize(_owner, _core, _wNative); + /// @param _jumpDisputeKitID The ID of the dispute kit to switch to after the court jump. + function initialize( + address _owner, + KlerosCore _core, + address _wNative, + uint256 _jumpDisputeKitID + ) external reinitializer(1) { + __DisputeKitClassicBase_initialize(_owner, _core, _wNative, _jumpDisputeKitID); } - function reinitialize(address _wNative) external reinitializer(9) { - wNative = _wNative; + function reinitialize(uint256 _jumpDisputeKitID) external reinitializer(10) { + jumpDisputeKitID = _jumpDisputeKitID; } // ************************ // diff --git a/contracts/src/arbitration/dispute-kits/DisputeKitSybilResistant.sol b/contracts/src/arbitration/dispute-kits/DisputeKitSybilResistant.sol index e213f6d11..66553f02a 100644 --- a/contracts/src/arbitration/dispute-kits/DisputeKitSybilResistant.sol +++ b/contracts/src/arbitration/dispute-kits/DisputeKitSybilResistant.sol @@ -18,7 +18,7 @@ interface IProofOfHumanity { /// - an incentive system: equal split between coherent votes, /// - an appeal system: fund 2 choices only, vote on any choice. contract DisputeKitSybilResistant is DisputeKitClassicBase { - string public constant override version = "0.12.0"; + string public constant override version = "0.13.0"; // ************************************* // // * Storage * // @@ -40,13 +40,15 @@ contract DisputeKitSybilResistant is DisputeKitClassicBase { /// @param _core The KlerosCore arbitrator. /// @param _poh The Proof of Humanity registry. /// @param _wNative The wrapped native token address, typically wETH. + /// @param _jumpDisputeKitID The ID of the dispute kit to switch to after the court jump. function initialize( address _owner, KlerosCore _core, IProofOfHumanity _poh, - address _wNative + address _wNative, + uint256 _jumpDisputeKitID ) external reinitializer(1) { - __DisputeKitClassicBase_initialize(_owner, _core, _wNative); + __DisputeKitClassicBase_initialize(_owner, _core, _wNative, _jumpDisputeKitID); poh = _poh; singleDrawPerJuror = true; } diff --git a/contracts/src/arbitration/interfaces/IDisputeKit.sol b/contracts/src/arbitration/interfaces/IDisputeKit.sol index 50e533203..90fbbd727 100644 --- a/contracts/src/arbitration/interfaces/IDisputeKit.sol +++ b/contracts/src/arbitration/interfaces/IDisputeKit.sol @@ -130,6 +130,10 @@ interface IDisputeKit { uint256 _currentNbVotes ) external view returns (uint256); // TODO: remove previousDisputeKit + /// @dev Returns the dispute kid ID be used after court jump by Kleros Core. + /// @return The ID of the dispute kit in Kleros Core disputeKits array. + function getJumpDisputeKitID() external view returns (uint256); + /// @dev Returns true if the specified voter was active in this round. /// @param _coreDisputeID The ID of the dispute in Kleros Core, not in the Dispute Kit. /// @param _coreRoundID The ID of the round in Kleros Core, not in the Dispute Kit. diff --git a/contracts/test/foundry/KlerosCore_Appeals.t.sol b/contracts/test/foundry/KlerosCore_Appeals.t.sol index f76649e03..6421cb119 100644 --- a/contracts/test/foundry/KlerosCore_Appeals.t.sol +++ b/contracts/test/foundry/KlerosCore_Appeals.t.sol @@ -146,7 +146,7 @@ contract KlerosCore_AppealsTest is KlerosCore_TestBase { disputeKit.fundAppeal{value: 0.1 ether}(disputeID, 2); } - function test_appeal_fullFundingNoSwitch() public { + function test_appeal_fullFundingNoJump() public { uint256 disputeID = 0; vm.prank(staker1); @@ -208,15 +208,18 @@ contract KlerosCore_AppealsTest is KlerosCore_TestBase { core.passPeriod(disputeID); } - function test_appeal_fullFundingDKCourtSwitch() public { + function test_appeal_fullFundingCourtJumpAndDKJumpToClassic() public { + // Setup: dk2 supported by court2 with dk2._jumpDisputeKitID == DISPUTE_KIT_CLASSIC + // Ensure that court2 jumps to GENERAL_COURT and dk2 jumps to DISPUTE_KIT_CLASSIC uint256 disputeID = 0; DisputeKitClassic dkLogic = new DisputeKitClassic(); - // Create a new DK and court to check the switch + // Create a new DK and court to check the jump bytes memory initDataDk = abi.encodeWithSignature( - "initialize(address,address,address)", + "initialize(address,address,address,uint256)", owner, address(core), - address(wNative) + address(wNative), + DISPUTE_KIT_CLASSIC ); UUPSProxy proxyDk = new UUPSProxy(address(dkLogic), initDataDk); @@ -249,6 +252,7 @@ contract KlerosCore_AppealsTest is KlerosCore_TestBase { supportedDK = new uint256[](1); supportedDK[0] = newDkID; core.enableDisputeKits(newCourtID, supportedDK, true); + assertEq(core.isSupported(newCourtID, newDkID), true, "New DK should be supported by new court"); vm.prank(staker1); core.setStake(newCourtID, 20000); @@ -278,7 +282,6 @@ contract KlerosCore_AppealsTest is KlerosCore_TestBase { vm.prank(crowdfunder1); newDisputeKit.fundAppeal{value: 0.63 ether}(disputeID, 1); - vm.prank(crowdfunder2); assertEq(core.isDisputeKitJumping(disputeID), true, "Should be jumping"); @@ -292,6 +295,7 @@ contract KlerosCore_AppealsTest is KlerosCore_TestBase { emit KlerosCoreBase.AppealDecision(disputeID, arbitrable); vm.expectEmit(true, true, true, true); emit KlerosCoreBase.NewPeriod(disputeID, KlerosCoreBase.Period.evidence); + vm.prank(crowdfunder2); newDisputeKit.fundAppeal{value: 0.42 ether}(disputeID, 2); (, bool jumped, ) = newDisputeKit.disputes(disputeID); @@ -325,6 +329,144 @@ contract KlerosCore_AppealsTest is KlerosCore_TestBase { assertEq(account, staker1, "Wrong drawn account in the classic DK"); } + function test_appeal_fullFundingCourtJumpAndDKJumpToNonClassic() public { + // Setup: + // dk2 supported by GENERAL_COURT, which is a non-DISPUTE_KIT_CLASSIC + // dk3 supported by court2, with dk3._jumpDisputeKitID == dk2 + // Ensure that court2 jumps to GENERAL_COURT and dk3 jumps to dk2 + uint256 disputeID = 0; + uint96 newCourtID = 2; + uint256 dkID2 = 2; + uint256 dkID3 = 3; + + DisputeKitClassic dkLogic = new DisputeKitClassic(); + + bytes memory initDataDk2 = abi.encodeWithSignature( + "initialize(address,address,address,uint256)", + owner, + address(core), + address(wNative), + DISPUTE_KIT_CLASSIC + ); + UUPSProxy proxyDk2 = new UUPSProxy(address(dkLogic), initDataDk2); + DisputeKitClassic disputeKit2 = DisputeKitClassic(address(proxyDk2)); + + bytes memory initDataDk3 = abi.encodeWithSignature( + "initialize(address,address,address,uint256)", + owner, + address(core), + address(wNative), + dkID2 + ); + UUPSProxy proxyDk3 = new UUPSProxy(address(dkLogic), initDataDk3); + DisputeKitClassic disputeKit3 = DisputeKitClassic(address(proxyDk3)); + + vm.prank(owner); + core.addNewDisputeKit(disputeKit2); + vm.prank(owner); + core.addNewDisputeKit(disputeKit3); + + uint256[] memory supportedDK = new uint256[](2); + supportedDK[0] = DISPUTE_KIT_CLASSIC; + supportedDK[1] = dkID3; + vm.prank(owner); + core.createCourt( + GENERAL_COURT, + hiddenVotes, + minStake, + alpha, + feeForJuror, + 3, // jurors for jump. Low number to ensure jump after the first appeal + [uint256(60), uint256(120), uint256(180), uint256(240)], // Times per period + sortitionExtraData, + supportedDK + ); + assertEq(core.isSupported(newCourtID, dkID3), true, "dkID3 should be supported by new court"); + + vm.prank(owner); + supportedDK[0] = DISPUTE_KIT_CLASSIC; + supportedDK[1] = dkID2; + core.enableDisputeKits(GENERAL_COURT, supportedDK, true); + assertEq(core.isSupported(GENERAL_COURT, dkID2), true, "dkID2 should be supported by GENERAL_COURT"); + + bytes memory newExtraData = abi.encodePacked(uint256(newCourtID), DEFAULT_NB_OF_JURORS, dkID3); + arbitrable.changeArbitratorExtraData(newExtraData); + + vm.prank(staker1); + core.setStake(newCourtID, 20000); + vm.prank(disputer); + arbitrable.createDispute{value: feeForJuror * DEFAULT_NB_OF_JURORS}("Action"); + vm.warp(block.timestamp + minStakingTime); + sortitionModule.passPhase(); // Generating + vm.warp(block.timestamp + rngLookahead); + sortitionModule.passPhase(); // Drawing phase + + KlerosCoreBase.Round memory round = core.getRoundInfo(disputeID, 0); + assertEq(round.disputeKitID, dkID3, "Wrong DK ID"); + + core.draw(disputeID, DEFAULT_NB_OF_JURORS); + vm.warp(block.timestamp + timesPerPeriod[0]); + core.passPeriod(disputeID); // Vote + + uint256[] memory voteIDs = new uint256[](3); + voteIDs[0] = 0; + voteIDs[1] = 1; + voteIDs[2] = 2; + + vm.prank(staker1); + disputeKit3.castVote(disputeID, voteIDs, 2, 0, "XYZ"); + + core.passPeriod(disputeID); // Appeal + + vm.prank(crowdfunder1); + disputeKit3.fundAppeal{value: 0.63 ether}(disputeID, 1); + + assertEq(core.isDisputeKitJumping(disputeID), true, "Should be jumping"); + + vm.expectEmit(true, true, true, true); + emit KlerosCoreBase.CourtJump(disputeID, 1, newCourtID, GENERAL_COURT); + vm.expectEmit(true, true, true, true); + emit KlerosCoreBase.DisputeKitJump(disputeID, 1, dkID3, dkID2); + vm.expectEmit(true, true, true, true); + emit DisputeKitClassicBase.DisputeCreation(disputeID, 2, newExtraData); + vm.expectEmit(true, true, true, true); + emit KlerosCoreBase.AppealDecision(disputeID, arbitrable); + vm.expectEmit(true, true, true, true); + emit KlerosCoreBase.NewPeriod(disputeID, KlerosCoreBase.Period.evidence); + vm.prank(crowdfunder2); + disputeKit3.fundAppeal{value: 0.42 ether}(disputeID, 2); + + (, bool jumped, ) = disputeKit3.disputes(disputeID); + assertEq(jumped, true, "jumped should be true"); + assertEq( + (disputeKit3.getFundedChoices(disputeID)).length, + 2, + "No fresh round created so the number of funded choices should be 2" + ); + + round = core.getRoundInfo(disputeID, 1); + assertEq(round.disputeKitID, dkID2, "Wrong DK ID"); + assertEq(sortitionModule.disputesWithoutJurors(), 1, "Wrong disputesWithoutJurors count"); + (uint96 courtID, , , , ) = core.disputes(disputeID); + assertEq(courtID, GENERAL_COURT, "Wrong court ID"); + + (, jumped, ) = disputeKit2.disputes(disputeID); + assertEq(jumped, false, "jumped should be false in the DK that dispute jumped to"); + + // Check jump modifier + vm.prank(address(core)); + vm.expectRevert(DisputeKitClassicBase.DisputeJumpedToParentDK.selector); + disputeKit3.draw(disputeID, 1); + + // And check that draw in the new round works + vm.expectEmit(true, true, true, true); + emit KlerosCoreBase.Draw(staker1, disputeID, 1, 0); // roundID = 1 VoteID = 0 + core.draw(disputeID, 1); + + (address account, , , ) = disputeKit2.getVoteInfo(disputeID, 1, 0); + assertEq(account, staker1, "Wrong drawn account in the classic DK"); + } + function test_appeal_quickPassPeriod() public { uint256 disputeID = 0; diff --git a/contracts/test/foundry/KlerosCore_Initialization.t.sol b/contracts/test/foundry/KlerosCore_Initialization.t.sol index 76711351e..d35190870 100644 --- a/contracts/test/foundry/KlerosCore_Initialization.t.sol +++ b/contracts/test/foundry/KlerosCore_Initialization.t.sol @@ -56,6 +56,8 @@ contract KlerosCore_InitializationTest is KlerosCore_TestBase { assertEq(pinakion.allowance(staker2, address(core)), 1 ether, "Wrong allowance for staker2"); assertEq(disputeKit.owner(), msg.sender, "Wrong DK owner"); + assertEq(disputeKit.getJumpDisputeKitID(), DISPUTE_KIT_CLASSIC, "Wrong jump DK"); + assertEq(disputeKit.jumpDisputeKitID(), DISPUTE_KIT_CLASSIC, "Wrong jump DK storage var"); assertEq(address(disputeKit.core()), address(core), "Wrong core in DK"); assertEq(sortitionModule.owner(), msg.sender, "Wrong SM owner"); @@ -110,10 +112,11 @@ contract KlerosCore_InitializationTest is KlerosCore_TestBase { UUPSProxy proxyCore = new UUPSProxy(address(coreLogic), ""); bytes memory initDataDk = abi.encodeWithSignature( - "initialize(address,address,address)", + "initialize(address,address,address,uint256)", newOwner, address(proxyCore), - address(wNative) + address(wNative), + DISPUTE_KIT_CLASSIC ); UUPSProxy proxyDk = new UUPSProxy(address(dkLogic), initDataDk); diff --git a/contracts/test/foundry/KlerosCore_TestBase.sol b/contracts/test/foundry/KlerosCore_TestBase.sol index ad297c86a..762d71db9 100644 --- a/contracts/test/foundry/KlerosCore_TestBase.sol +++ b/contracts/test/foundry/KlerosCore_TestBase.sol @@ -115,10 +115,11 @@ abstract contract KlerosCore_TestBase is Test { UUPSProxy proxyCore = new UUPSProxy(address(coreLogic), ""); bytes memory initDataDk = abi.encodeWithSignature( - "initialize(address,address,address)", + "initialize(address,address,address,uint256)", owner, address(proxyCore), - address(wNative) + address(wNative), + DISPUTE_KIT_CLASSIC ); UUPSProxy proxyDk = new UUPSProxy(address(dkLogic), initDataDk); diff --git a/contracts/test/foundry/KlerosCore_Voting.t.sol b/contracts/test/foundry/KlerosCore_Voting.t.sol index 9ede69305..7d84f18e1 100644 --- a/contracts/test/foundry/KlerosCore_Voting.t.sol +++ b/contracts/test/foundry/KlerosCore_Voting.t.sol @@ -388,10 +388,11 @@ contract KlerosCore_VotingTest is KlerosCore_TestBase { DisputeKitClassic dkLogic = new DisputeKitClassic(); // Create a new DK to check castVote. bytes memory initDataDk = abi.encodeWithSignature( - "initialize(address,address,address)", + "initialize(address,address,address,uint256)", owner, address(core), - address(wNative) + address(wNative), + DISPUTE_KIT_CLASSIC ); UUPSProxy proxyDk = new UUPSProxy(address(dkLogic), initDataDk);