@@ -1667,4 +1667,173 @@ contract KlerosCore_AppealsTest is KlerosCore_TestBase {
16671667 round = core.getRoundInfo (disputeID, 1 );
16681668 assertEq (round.drawnJurors.length , customNbVotes, "Should have drawn 9 jurors in Court2 " );
16691669 }
1670+
1671+ /// @dev Test that jumpDisputeKitID takes precedence over jumpDisputeKitIDOnCourtJump when both are set
1672+ function test_appeal_jumpDisputeKitIDTakesPrecedenceOverOnCourtJump () public {
1673+ uint256 disputeID = 0 ;
1674+ uint96 court2ID = 2 ;
1675+ uint96 court3ID = 3 ;
1676+ uint256 dkID2 = 2 ;
1677+ uint256 dkID3 = 3 ;
1678+
1679+ // Create DisputeKit2 and DisputeKit3
1680+ DisputeKitClassic dkLogic = new DisputeKitClassic ();
1681+
1682+ bytes memory initDataDk2 = abi.encodeWithSignature (
1683+ "initialize(address,address,address) " ,
1684+ owner,
1685+ address (core),
1686+ address (wNative)
1687+ );
1688+ UUPSProxy proxyDk2 = new UUPSProxy (address (dkLogic), initDataDk2);
1689+ DisputeKitClassic disputeKit2 = DisputeKitClassic (address (proxyDk2));
1690+
1691+ bytes memory initDataDk3 = abi.encodeWithSignature (
1692+ "initialize(address,address,address) " ,
1693+ owner,
1694+ address (core),
1695+ address (wNative)
1696+ );
1697+ UUPSProxy proxyDk3 = new UUPSProxy (address (dkLogic), initDataDk3);
1698+ DisputeKitClassic disputeKit3 = DisputeKitClassic (address (proxyDk3));
1699+
1700+ vm.prank (owner);
1701+ core.addNewDisputeKit (disputeKit2);
1702+ vm.prank (owner);
1703+ core.addNewDisputeKit (disputeKit3);
1704+
1705+ // Create Court2 supporting all 3 DKs
1706+ uint256 [] memory supportedDK = new uint256 [](3 );
1707+ supportedDK[0 ] = DISPUTE_KIT_CLASSIC;
1708+ supportedDK[1 ] = dkID2;
1709+ supportedDK[2 ] = dkID3;
1710+ vm.prank (owner);
1711+ core.createCourt (
1712+ GENERAL_COURT, // parent
1713+ hiddenVotes,
1714+ minStake,
1715+ alpha,
1716+ 0.05 ether,
1717+ 3 , // Low jurorsForCourtJump to ensure jump
1718+ [uint256 (60 ), uint256 (120 ), uint256 (180 ), uint256 (240 )],
1719+ sortitionExtraData,
1720+ supportedDK
1721+ );
1722+
1723+ // Create Court3 (sibling of Court2) also supporting all 3 DKs
1724+ vm.prank (owner);
1725+ core.createCourt (
1726+ GENERAL_COURT, // Same parent as Court2 (siblings)
1727+ hiddenVotes,
1728+ minStake,
1729+ alpha,
1730+ 0.07 ether,
1731+ 5 ,
1732+ [uint256 (60 ), uint256 (120 ), uint256 (180 ), uint256 (240 )],
1733+ sortitionExtraData,
1734+ supportedDK
1735+ );
1736+
1737+ // CRITICAL: Configure NextRoundSettings with BOTH jumpDisputeKitID and jumpDisputeKitIDOnCourtJump set
1738+ // jumpDisputeKitID should take precedence
1739+ vm.prank (owner);
1740+ disputeKit3.changeNextRoundSettings (
1741+ court2ID,
1742+ DisputeKitClassicBase.NextRoundSettings ({
1743+ enabled: true ,
1744+ jumpCourtID: court3ID, // Force court jump to Court3
1745+ jumpDisputeKitID: dkID2, // Should be used (takes precedence)
1746+ jumpDisputeKitIDOnCourtJump: dkID3, // Should be IGNORED despite being set
1747+ nbVotes: 0
1748+ })
1749+ );
1750+
1751+ // Stake in courts
1752+ vm.prank (staker1);
1753+ core.setStake (court2ID, 20000 );
1754+ vm.prank (staker1);
1755+ core.setStake (court3ID, 20000 );
1756+
1757+ // Create dispute in Court2 with DisputeKit3
1758+ bytes memory extraData = abi.encodePacked (uint256 (court2ID), DEFAULT_NB_OF_JURORS, dkID3);
1759+ arbitrable.changeArbitratorExtraData (extraData);
1760+ vm.prank (disputer);
1761+ arbitrable.createDispute {value: 0.05 ether * DEFAULT_NB_OF_JURORS}("Action " );
1762+
1763+ vm.warp (block .timestamp + minStakingTime);
1764+ sortitionModule.passPhase (); // Generating
1765+ vm.warp (block .timestamp + rngLookahead);
1766+ sortitionModule.passPhase (); // Drawing
1767+
1768+ // Verify initial round uses DisputeKit3
1769+ KlerosCore.Round memory round = core.getRoundInfo (disputeID, 0 );
1770+ assertEq (round.disputeKitID, dkID3, "Initial round should use DisputeKit3 " );
1771+
1772+ core.draw (disputeID, DEFAULT_NB_OF_JURORS);
1773+ vm.warp (block .timestamp + timesPerPeriod[0 ]);
1774+ core.passPeriod (disputeID); // Vote
1775+
1776+ uint256 [] memory voteIDs = new uint256 [](3 );
1777+ voteIDs[0 ] = 0 ;
1778+ voteIDs[1 ] = 1 ;
1779+ voteIDs[2 ] = 2 ;
1780+ vm.prank (staker1);
1781+ disputeKit3.castVote (disputeID, voteIDs, 2 , 0 , "XYZ " );
1782+
1783+ core.passPeriod (disputeID); // Appeal
1784+
1785+ // CRITICAL TEST: Verify jump prediction shows DK2, NOT DK3
1786+ (uint96 nextCourtID , uint256 nextDisputeKitID , , bool isCourtJumping , bool isDisputeKitJumping ) = core
1787+ .getCourtAndDisputeKitJumps (disputeID);
1788+ assertEq (nextCourtID, court3ID, "Should jump to Court3 " );
1789+ assertEq (nextDisputeKitID, dkID2, "Should jump to DisputeKit2 (jumpDisputeKitID), NOT DisputeKit3 " );
1790+ assertEq (isCourtJumping, true , "Should be court jumping " );
1791+ assertEq (isDisputeKitJumping, true , "Should be DK jumping " );
1792+
1793+ // Verify appealCost uses Court3's feeForJuror
1794+ uint256 expectedCost = 0.07 ether * 7 ; // 0.49 ether
1795+ assertEq (core.appealCost (disputeID), expectedCost, "appealCost should use Court3's fee " );
1796+
1797+ // Fund and execute appeal
1798+ vm.prank (crowdfunder1);
1799+ disputeKit3.fundAppeal {value: 1.47 ether }(disputeID, 1 );
1800+
1801+ // Verify events show jump to Court3 and DisputeKit2 (NOT DisputeKit3)
1802+ vm.expectEmit (true , true , true , true );
1803+ emit KlerosCore.CourtJump (disputeID, 1 , court2ID, court3ID);
1804+ vm.expectEmit (true , true , true , true );
1805+ emit KlerosCore.DisputeKitJump (disputeID, 1 , dkID3, dkID2); // DK3 -> DK2 (NOT DK3)
1806+ vm.expectEmit (true , true , true , true );
1807+ emit DisputeKitClassicBase.DisputeCreation (disputeID, 2 , extraData);
1808+ vm.expectEmit (true , true , true , true );
1809+ emit KlerosCore.AppealDecision (disputeID, arbitrable);
1810+ vm.expectEmit (true , true , true , true );
1811+ emit KlerosCore.NewPeriod (disputeID, KlerosCore.Period.evidence);
1812+ vm.prank (crowdfunder2);
1813+ disputeKit3.fundAppeal {value: 0.98 ether }(disputeID, 2 );
1814+
1815+ // Verify dispute is now in Court3 with DisputeKit2
1816+ (uint96 courtID , , , , ) = core.disputes (disputeID);
1817+ assertEq (courtID, court3ID, "Dispute should be in Court3 " );
1818+
1819+ round = core.getRoundInfo (disputeID, 1 );
1820+ assertEq (round.disputeKitID, dkID2, "New round should use DisputeKit2 (NOT DisputeKit3) " );
1821+ assertEq (round.nbVotes, 7 , "New round should have 7 jurors " );
1822+
1823+ // Verify DisputeKit3 is no longer active for this dispute
1824+ (, bool currentRound ) = disputeKit3.coreDisputeIDToActive (disputeID);
1825+ assertEq (currentRound, false , "DisputeKit3 should no longer be active " );
1826+
1827+ // Verify DisputeKit2 is now active
1828+ (, currentRound) = disputeKit2.coreDisputeIDToActive (disputeID);
1829+ assertEq (currentRound, true , "DisputeKit2 should be active " );
1830+
1831+ // Verify we can draw jurors in the new DK
1832+ vm.expectEmit (true , true , true , true );
1833+ emit KlerosCore.Draw (staker1, disputeID, 1 , 0 );
1834+ core.draw (disputeID, 1 );
1835+
1836+ (address account , , , ) = disputeKit2.getVoteInfo (disputeID, 1 , 0 );
1837+ assertEq (account, staker1, "Should have drawn juror in DisputeKit2 " );
1838+ }
16701839}
0 commit comments