Skip to content

Conversation

@jaybuidl
Copy link
Member

@jaybuidl jaybuidl commented Oct 21, 2025

PR-Codex overview

This PR focuses on refining the initialization parameters for various DisputeKit contracts by removing unnecessary arguments, enhancing the optimization settings, and implementing new logic for dispute kit transitions. It also adds tests for the new functionality.

Detailed summary

  • Removed jumpDisputeKitID argument from initialize functions in several DisputeKit contracts.
  • Updated optimizer runs from 800 to 1000 in hardhat.config.ts.
  • Simplified deployment arguments for DisputeKitClassic, DisputeKitShutter, and DisputeKitGated.
  • Introduced NextRoundSettings struct in DisputeKitClassicBase for managing round settings.
  • Updated getNextRoundSettings method to accommodate new logic for determining court and dispute kit transitions.
  • Added tests for new functionality in KlerosCore_AppealsTest, including scenarios for court jumps and dispute kit changes.
  • Ensured compatibility checks are enforced during dispute transitions.
  • Implemented fallbacks for invalid jump settings and custom voting scenarios.

The following files were skipped due to too many changes: contracts/test/foundry/KlerosCore_Appeals.t.sol

✨ Ask PR-Codex anything about this PR by commenting with /codex {your question}

Summary by CodeRabbit

  • New Features

    • Public getter now returns next court, next dispute‑kit and resulting vote count; draw also returns originating subcourt ID.
  • Refactor

    • Appeal/jump flow consolidated around compatibility‑based next‑round settings.
    • Per‑court NextRoundSettings added, configurable and emitting events; legacy jump helpers removed.
  • Chore

    • Initializer signatures simplified across dispute‑kit variants; deployments/tests updated to match.
    • Solidity optimizer runs increased.
  • Tests

    • Expanded appeal/jump test coverage, edge cases, and cost/funding scenarios.

@jaybuidl jaybuidl self-assigned this Oct 21, 2025
@jaybuidl jaybuidl added Type: Feature🗿 Compatibility: ABI change 🗯 Smart contract ABI is changing. Package: Contracts Court smart contracts labels Oct 21, 2025
@netlify
Copy link

netlify bot commented Oct 21, 2025

Deploy Preview for kleros-v2-neo ready!

Name Link
🔨 Latest commit 6d76559
🔍 Latest deploy log https://app.netlify.com/projects/kleros-v2-neo/deploys/68fbfb10b2503f00088cd56e
😎 Deploy Preview https://deploy-preview-2187--kleros-v2-neo.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify
Copy link

netlify bot commented Oct 21, 2025

Deploy Preview for kleros-v2-testnet ready!

Name Link
🔨 Latest commit 6d76559
🔍 Latest deploy log https://app.netlify.com/projects/kleros-v2-testnet/deploys/68fbfb10b7be5e0008d9270b
😎 Deploy Preview https://deploy-preview-2187--kleros-v2-testnet.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify
Copy link

netlify bot commented Oct 21, 2025

Deploy Preview for kleros-v2-testnet-devtools failed. Why did it fail? →

Name Link
🔨 Latest commit 6d76559
🔍 Latest deploy log https://app.netlify.com/projects/kleros-v2-testnet-devtools/deploys/68fbfb1072b0f0000863044c

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 21, 2025

Walkthrough

Replaces global per-dispute jump ID with per‑court NextRoundSettings, consolidates next‑round API to return (newCourtID, newDisputeKitID, newRoundNbVotes), removes _jumpDisputeKitID from dispute‑kit initializers/deployments/tests, extends draw to return fromSubcourtID, and bumps Solidity optimizer runs to 1000.

Changes

Cohort / File(s) Summary
Config
contracts/hardhat.config.ts
Increased Solidity optimizer runs from 800 to 1000.
Core Arbitration
contracts/src/arbitration/KlerosCore.sol
Replaced legacy court/dispute‑kit jump helpers with compatibility‑based _getCompatibleNextRoundSettings(...); added external getCourtAndDisputeKitJumps(uint256) returning (newCourtID,newDisputeKitID,newRoundNbVotes,courtJump,disputeKitJump); updated appeals, appealCost, and CourtJump emission sites.
DisputeKit Base
contracts/src/arbitration/dispute-kits/DisputeKitClassicBase.sol
Removed global jumpDisputeKitID; added NextRoundSettings struct and mapping(uint96 => NextRoundSettings) public courtIDToNextRoundSettings; added changeNextRoundSettings(...) and NextRoundSettingsChanged event; implemented getNextRoundSettings(...); initializer no longer accepts _jumpDisputeKitID.
DisputeKit Implementations
contracts/src/arbitration/dispute-kits/DisputeKitClassic.sol, .../DisputeKitGated.sol, .../DisputeKitGatedShutter.sol, .../DisputeKitShutter.sol, .../DisputeKitSybilResistant.sol
Removed _jumpDisputeKitID parameter from initialize signatures and updated calls to base initializer to omit the jump ID.
DisputeKit Interface
contracts/src/arbitration/interfaces/IDisputeKit.sol
Extended draw(...) return to include uint96 fromSubcourtID; added getNextRoundSettings(...); removed earlyCourtJump, getNbVotesAfterAppeal, and getJumpDisputeKitID.
Deployments
contracts/deploy/...00-home-chain-arbitration*.ts
Shortened deployment args for multiple DisputeKit upgradable deployments (removed classic/dispute‑kit ID arguments).
Tests / Mocks
contracts/test/*, contracts/test/arbitration/helpers/*, contracts/test/foundry/*, contracts/src/test/DisputeKitClassicMockUncheckedNextRoundSettings.sol
Updated proxy initializer calldata and test deployments to new initialize(...) signatures (removed uint256 arg); added changeNextRoundSettings usage in tests; adapted assertions to new getCourtAndDisputeKitJumps tuple and updated draw expectations; added mock test contract overriding getNextRoundSettings.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant KlerosCore
    participant DisputeKit

    rect rgba(200,230,255,0.12)
    Note over User,KlerosCore: Appeal funding → determine next‑round settings
    User->>KlerosCore: fundAppeal(disputeID,...)
    KlerosCore->>DisputeKit: getNextRoundSettings(coreDisputeID,currentCourtID,parentCourtID,currentCourtJurorsForJump,currentDisputeKitID,currentRoundNbVotes)
    DisputeKit->>DisputeKit: read courtIDToNextRoundSettings[currentCourtID] & compatibility checks
    alt Compatible
        DisputeKit-->>KlerosCore: (newCourtID,newDisputeKitID,newRoundNbVotes)
    else Incompatible / fallback
        DisputeKit-->>KlerosCore: (classicCourtID,classicDisputeKitID,adjustedNbVotes)
    end
    KlerosCore-->>User: proceed with updated court/DK/votes (emit CourtJump if newCourtID != current)
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • unknownunknown1

Poem

🐰 I hopped through mappings, court by court,
Swapped one global for settings that sort.
Draw now tells whence each juror came,
Next‑round rules wear a single name.
Little rabbit cheers — new rounds play smart.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The PR title "Increased Court and DK jump logic flexibility" accurately summarizes the primary objective and main changes in the pull request. The changeset fundamentally refactors the dispute-kit and court jump mechanisms by introducing a configurable NextRoundSettings structure, replacing hard-coded jump behavior with per-court configuration via changeNextRoundSettings, and replacing rigid jump-related functions (earlyCourtJump, getJumpDisputeKitID) with the more flexible getNextRoundSettings interface. The title is specific and clear—it identifies both the domain (court and DK jump logic) and the improvement (increased flexibility)—allowing a reviewer scanning the history to immediately understand the primary change without ambiguity.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/next-round-settings-in-dk

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c40c12c and 9ea9e98.

📒 Files selected for processing (9)
  • contracts/hardhat.config.ts (1 hunks)
  • contracts/src/arbitration/KlerosCore.sol (4 hunks)
  • contracts/src/arbitration/dispute-kits/DisputeKitClassic.sol (1 hunks)
  • contracts/src/arbitration/dispute-kits/DisputeKitClassicBase.sol (6 hunks)
  • contracts/src/arbitration/dispute-kits/DisputeKitGated.sol (1 hunks)
  • contracts/src/arbitration/dispute-kits/DisputeKitGatedShutter.sol (1 hunks)
  • contracts/src/arbitration/dispute-kits/DisputeKitShutter.sol (1 hunks)
  • contracts/src/arbitration/dispute-kits/DisputeKitSybilResistant.sol (1 hunks)
  • contracts/src/arbitration/interfaces/IDisputeKit.sol (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (14)
  • GitHub Check: Redirect rules - kleros-v2-testnet
  • GitHub Check: Redirect rules - kleros-v2-testnet
  • GitHub Check: Redirect rules - kleros-v2-testnet-devtools
  • GitHub Check: Header rules - kleros-v2-testnet
  • GitHub Check: Redirect rules - kleros-v2-neo
  • GitHub Check: Header rules - kleros-v2-testnet
  • GitHub Check: Header rules - kleros-v2-testnet-devtools
  • GitHub Check: Pages changed - kleros-v2-testnet
  • GitHub Check: Header rules - kleros-v2-neo
  • GitHub Check: Pages changed - kleros-v2-testnet
  • GitHub Check: Pages changed - kleros-v2-testnet-devtools
  • GitHub Check: Pages changed - kleros-v2-neo
  • GitHub Check: Analyze (javascript)
  • GitHub Check: hardhat-tests
🔇 Additional comments (15)
contracts/src/arbitration/dispute-kits/DisputeKitGated.sol (1)

53-54: LGTM! Initializer signature updated consistently.

The removal of the _jumpDisputeKitID parameter aligns with the PR's shift to per-court next-round settings, and the base initializer call is correctly updated.

contracts/src/arbitration/dispute-kits/DisputeKitSybilResistant.sol (1)

47-49: LGTM! Consistent initializer update.

The parameter removal and updated base initializer call are consistent with the PR's architectural changes.

contracts/src/arbitration/dispute-kits/DisputeKitClassic.sol (1)

29-30: LGTM! Initializer signature simplified.

Consistent with the removal of global jump settings across all dispute kit implementations.

contracts/src/arbitration/dispute-kits/DisputeKitShutter.sol (1)

63-64: LGTM! Initializer updated consistently.

The signature change matches the pattern across all dispute kit implementations in this PR.

contracts/src/arbitration/dispute-kits/DisputeKitGatedShutter.sol (1)

82-83: LGTM! Initializer signature updated.

Consistent with the initialization changes across all dispute kit variants.

contracts/src/arbitration/KlerosCore.sol (3)

788-793: Verify unused return values are intentional.

The call to _getCourtAndDisputeKitJumps ignores newRoundNbVotes (line 788) and disputeKitJump (line 788) with underscores. While courtJump is used on line 794, confirm that disputeKitJump doesn't need to be emitted or tracked here. The DisputeKitJump event is emitted later at line 812, but only conditionally when the dispute kit actually changes.


1089-1094: LGTM! Return values properly utilized.

The function correctly unpacks all 5 return values from _getCourtAndDisputeKitJumps and uses nbVotesAfterAppeal and courtJump to compute the appeal cost. The ignored values (newCourtID and disputeKitJump) are appropriately not needed for cost calculation.


1183-1213: LGTM! Public getter provides transparency.

The new public getCourtAndDisputeKitJumps function appropriately exposes the jump logic for external callers and front-ends, enabling better visibility into dispute progression.

contracts/hardhat.config.ts (1)

35-35: Verify contract size remains under 24KB limit through proper build compilation.

The review comment's concern is valid—24.576 KB is the contract size limit on Ethereum—and the hardhat config confirms optimizer runs are "Constrained by the size of the KlerosCore contract."

Verification could not be completed in sandbox (compilation failed), but code analysis shows the PR changes are optimizations rather than bloat:

  • Removed isDisputeKitJumping() function (bytecode savings)
  • Added getCourtAndDisputeKitJumps() function (bytecode cost)
  • Removed external call to disputeKits[].getNbVotesAfterAppeal() and inlined the logic (further savings)

The net bytecode impact likely favors the changes. However, you should verify this locally by compiling and checking the deployed bytecode size before merging to confirm the contract remains compliant with the 24KB limit.

contracts/src/arbitration/interfaces/IDisputeKit.sol (2)

54-58: LGTM: Enhanced draw function signature.

The additional fromSubcourtID return value enables tracking which subcourt a juror was drawn from, which appears necessary for the new per-court settings logic.


127-154: Well-designed unified jump determination function.

This function consolidates the previously separate jump-related helpers (getJumpDisputeKitID, earlyCourtJump, getNbVotesAfterAppeal) into a single, comprehensive interface that returns all necessary jump information in one call. The documentation clearly states that compatibility validation is the Core's responsibility.

contracts/src/arbitration/dispute-kits/DisputeKitClassicBase.sol (4)

61-68: Well-structured settings with fine-grained control.

The NextRoundSettings struct provides comprehensive per-court configuration with clear semantics. The enabled flag allows toggling settings without data loss, and the separate earlyDisputeKitJump and earlyCourtJump flags enable nuanced control over jump behavior.


631-670: Well-designed jump logic with appropriate fallbacks.

The implementation correctly handles multiple scenarios:

  1. Uses per-court settings when enabled
  2. Falls back to default behavior (vote doubling, parent court on jump, DISPUTE_KIT_CLASSIC) when settings are absent or zero
  3. Combines early jump flags with threshold-based jump detection (line 661-663), allowing jumps to occur via either mechanism

The logic flow ensures that even with partial settings, the function returns valid values.


433-434: No issues found — KlerosCore signature is correct.

KlerosCore.getCourtAndDisputeKitJumps at line 1190 accepts a single uint256 _disputeID parameter and returns five values: uint96 newCourtID, uint256 newDisputeKitID, uint256 newRoundNbVotes, bool courtJump, and bool disputeKitJump. The call in DisputeKitClassicBase.sol:433-434 correctly invokes this function with _coreDisputeID and destructures the fifth return value (isDisputeKitJumping) as expected.


161-169: Verify operational setup and document migration path for per-court dispute kit settings.

The architectural change from global _jumpDisputeKitID to per-court courtIDToNextRoundSettings introduces an operational concern. Verification confirms:

  1. No automatic initialization: The populateCourts script and deployment process do not call changeNextRoundSettings. Per-court settings must be manually configured via governance.

  2. Default behavior: Courts fall back to DISPUTE_KIT_CLASSIC until explicitly configured (line 668), which may be acceptable but is undocumented.

  3. Missing migration documentation: No deployment guide or migration script shows how to initialize settings for existing or newly deployed courts.

Recommended actions:

  • Document the expected post-deployment/upgrade process for initializing per-court settings
  • Consider automating this in populateCourts or provide a dedicated migration task
  • Ensure governance has clear procedures for configuring settings per court
  • Add safeguards or validation to prevent courts from operating in unconfigured state unintentionally

@jaybuidl jaybuidl force-pushed the feat/next-round-settings-in-dk branch from 9ea9e98 to fac9901 Compare October 21, 2025 18:24
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
contracts/src/arbitration/KlerosCore.sol (1)

788-806: Use DK-computed vote count in appeal() to align with appealCost() estimation.

The inconsistency is confirmed. appealCost() (lines 1089, 1102, 1106) uses the dispute kit's computed nbVotesAfterAppeal, while appeal() (line 803) ignores the DK-returned newRoundNbVotes and instead calculates extraRound.nbVotes = msg.value / court.feeForJuror. This breaks the dispute kit abstraction—if the DK applies custom vote logic (thresholds, multipliers, rounding), the cost estimate and actual allocation diverge, potentially allowing overpayment to inflate vote counts beyond what the DK computed.

Recommended fix:

-        (uint96 newCourtID, uint256 newDisputeKitID, , bool courtJump, ) = _getCourtAndDisputeKitJumps(
+        (uint96 newCourtID, uint256 newDisputeKitID, uint256 newRoundNbVotes, bool courtJump, ) = _getCourtAndDisputeKitJumps(
             dispute,
             round,
             courts[dispute.courtID],
             _disputeID
         );
         // ...
         Court storage court = courts[newCourtID];
-        extraRound.nbVotes = msg.value / court.feeForJuror;
+        extraRound.nbVotes = newRoundNbVotes;
+        uint256 requiredFee = court.feeForJuror * newRoundNbVotes;
+        if (msg.value < requiredFee) revert AppealFeesNotEnough();
♻️ Duplicate comments (2)
contracts/src/arbitration/KlerosCore.sol (1)

1269-1274: Verify newRoundNbVotes consistency after dispute kit fallback.

When the compatibility check (lines 1270-1274) falls back to DISPUTE_KIT_CLASSIC because the new court doesn't support the originally proposed dispute kit, the newRoundNbVotes value is not recalculated. This value was computed by the original dispute kit's getCourtAndDisputeKitJumps implementation, which might use different logic than what DISPUTE_KIT_CLASSIC would compute.

Scenario:

// Original DK (e.g., specialized kit) returns:
// newDisputeKitID = 5 (not supported by new court)
// newRoundNbVotes = 100 (calculated by specialized kit's logic)

// After fallback:
// newDisputeKitID = DISPUTE_KIT_CLASSIC (forced)
// newRoundNbVotes = 100 (still from specialized kit's logic)
// But DISPUTE_KIT_CLASSIC might compute it as 50 with its own logic

Based on learnings from past review comments, this issue was previously identified. Confirm whether:

  1. The number of votes for a round is standardized and doesn't depend on the specific dispute kit implementation, OR
  2. The newRoundNbVotes should be recalculated by calling getCourtAndDisputeKitJumps again on the DISPUTE_KIT_CLASSIC instance after the fallback

If recalculation is needed:

 // Ensure compatibility between the next round's court and dispute kit.
 if (!courts[newCourtID].supportedDisputeKits[newDisputeKitID] || newDisputeKitID == NULL_DISPUTE_KIT) {
     // Fall back to `DisputeKitClassic` which is always supported.
     newDisputeKitID = DISPUTE_KIT_CLASSIC;
     disputeKitJump = (newDisputeKitID != disputeKitID);
+    // Recalculate votes with the fallback dispute kit
+    (, , newRoundNbVotes, , ) = disputeKits[DISPUTE_KIT_CLASSIC].getCourtAndDisputeKitJumps(
+        _disputeID,
+        _dispute.courtID,
+        _court.parent,
+        _court.jurorsForCourtJump,
+        _round.nbVotes
+    );
 }
contracts/src/arbitration/dispute-kits/DisputeKitClassicBase.sol (1)

196-204: Validation and event emission still needed (echoing past review).

As noted in the previous review, this governance function should:

  1. Validate that court IDs and dispute kit IDs exist (at least basic bounds checking)
  2. Emit an event for off-chain monitoring and transparency

The past review comment provided a concrete suggestion with validation and an event. While the interface notes that Core handles compatibility checking (Line 128 in IDisputeKit), basic validation here prevents governance configuration errors.

Based on learnings (past review comments).

🧹 Nitpick comments (2)
contracts/deploy/00-home-chain-arbitration-mainnet.ts (1)

144-144: Address the TODO comment in the context of this PR.

The TODO comment asks about jumping to a Shutter DK instead of Classic. Since this PR introduces flexible per-court jump configuration via NextRoundSettings (see DisputeKitClassicBase.courtIDToNextRoundSettings), this is the ideal time to resolve this question. The default behavior can now be configured per-court after deployment using changeNextRoundSettings, but clarifying the intended default during initialization would improve code clarity.

Consider either:

  1. Removing the TODO if the per-court settings make this flexible enough
  2. Setting an explicit jumpDisputeKitID in the NextRoundSettings for courts using DisputeKitGatedShutter

Would you like me to search the codebase for how other dispute kits configure their jump targets?

contracts/src/arbitration/dispute-kits/DisputeKitClassicBase.sol (1)

631-670: Consider adding inline comments to clarify the decision flow.

The implementation correctly handles the complex interdependencies between nbVotes, courtJump, newCourtID, and newDisputeKitID. However, the logic flow has several non-obvious aspects:

  1. Line 658: The condition if (nextRoundSettings.nbVotes == 0) applies the default doubling formula even when enabled=true, which is correct but might be unexpected.

  2. Line 662: The condition if (!courtJump) means "if not explicitly set to true by earlyCourtJump", allowing the threshold check to proceed. This subtle override logic could use a comment.

  3. Lines 664-666: The newCourtID determination depends on the computed courtJump value, creating a dependency chain: newRoundNbVotes → threshold check → courtJumpnewCourtID selection.

Consider adding inline comments like:

+        // Use settings if configured, otherwise apply defaults
         if (nextRoundSettings.nbVotes == 0) {
+            // Default: double the votes and add 1
             newRoundNbVotes = (_currentRoundNbVotes * 2) + 1;
         }
         if (!courtJump) {
+            // Check threshold-based jump if not explicitly requested via earlyCourtJump
             courtJump = (newRoundNbVotes >= _currentCourtJurorsForJump);
         }
         if (newCourtID == 0) {
+            // Jump to parent court if threshold exceeded, otherwise stay in current
             newCourtID = courtJump ? _parentCourtID : _currentCourtID;
         }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9ea9e98 and 973f572.

📒 Files selected for processing (14)
  • contracts/deploy/00-home-chain-arbitration-mainnet.ts (2 hunks)
  • contracts/deploy/00-home-chain-arbitration-university.ts (1 hunks)
  • contracts/deploy/00-home-chain-arbitration.ts (4 hunks)
  • contracts/hardhat.config.ts (1 hunks)
  • contracts/src/arbitration/KlerosCore.sol (4 hunks)
  • contracts/src/arbitration/dispute-kits/DisputeKitClassic.sol (1 hunks)
  • contracts/src/arbitration/dispute-kits/DisputeKitClassicBase.sol (6 hunks)
  • contracts/src/arbitration/dispute-kits/DisputeKitGated.sol (1 hunks)
  • contracts/src/arbitration/dispute-kits/DisputeKitGatedShutter.sol (1 hunks)
  • contracts/src/arbitration/dispute-kits/DisputeKitShutter.sol (1 hunks)
  • contracts/src/arbitration/dispute-kits/DisputeKitSybilResistant.sol (1 hunks)
  • contracts/src/arbitration/interfaces/IDisputeKit.sol (2 hunks)
  • contracts/test/arbitration/helpers/dispute-kit-gated-common.ts (1 hunks)
  • contracts/test/arbitration/helpers/dispute-kit-shutter-common.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • contracts/src/arbitration/dispute-kits/DisputeKitGated.sol
  • contracts/src/arbitration/dispute-kits/DisputeKitSybilResistant.sol
  • contracts/src/arbitration/dispute-kits/DisputeKitShutter.sol
  • contracts/hardhat.config.ts
🧰 Additional context used
🧬 Code graph analysis (1)
contracts/deploy/00-home-chain-arbitration-mainnet.ts (2)
contracts/deploy/utils/deployUpgradable.ts (1)
  • deployUpgradable (37-89)
contracts/deployments/utils.ts (1)
  • deployments (3-16)
🔇 Additional comments (18)
contracts/deploy/00-home-chain-arbitration.ts (4)

40-40: LGTM! Deployment args correctly updated.

The removal of the fourth argument aligns with the updated initialize signature in DisputeKitClassic that no longer accepts _jumpDisputeKitID.


118-118: LGTM! Deployment args correctly updated.

The removal of the fourth argument aligns with the updated initialize signature in DisputeKitShutter.


127-127: LGTM! Deployment args correctly updated.

The removal of the fourth argument aligns with the updated initialize signature in DisputeKitGated.


136-136: Address the TODO comment regarding DK jump behavior.

The TODO suggests considering a jump to a Shutter DK instead of a Classic one. With the new per-court NextRoundSettings approach, this configuration should now be handled via changeNextRoundSettings rather than at initialization time. Consider whether this TODO is still relevant or should be updated/removed.

contracts/deploy/00-home-chain-arbitration-university.ts (1)

37-37: LGTM! Deployment args correctly updated.

The removal of the fourth argument aligns with the updated initialize signature for DisputeKitClassic.

contracts/test/arbitration/helpers/dispute-kit-gated-common.ts (1)

183-183: LGTM! Test setup correctly updated.

The removal of the fourth argument aligns with the updated dispute kit initialization signatures.

contracts/test/arbitration/helpers/dispute-kit-shutter-common.ts (1)

249-249: LGTM! Test setup correctly updated.

The removal of the fourth argument aligns with the updated initialize signature for DisputeKitGatedShutter.

contracts/src/arbitration/dispute-kits/DisputeKitGatedShutter.sol (1)

82-84: LGTM! Initializer correctly refactored.

The removal of _jumpDisputeKitID parameter and the updated base initializer call align with the new per-court NextRoundSettings design, where jump behavior is configured per court rather than globally at initialization.

contracts/src/arbitration/dispute-kits/DisputeKitClassic.sol (1)

29-30: LGTM! Initializer correctly refactored.

The removal of _jumpDisputeKitID parameter and the updated base initializer call align with the new per-court NextRoundSettings design.

contracts/src/arbitration/KlerosCore.sol (1)

1183-1213: LGTM! Public getter properly delegates to internal helper.

The new public getCourtAndDisputeKitJumps function correctly exposes the internal jump logic for external callers, maintaining consistent behavior with internal usage.

contracts/src/arbitration/interfaces/IDisputeKit.sol (2)

54-58: LGTM! Draw signature extended for subcourt tracking.

The additional fromSubcourtID return value enables KlerosCore to track which subcourt a juror was drawn from, supporting more granular stake management.


127-154: LGTM! Consolidated jump logic improves interface clarity.

The new getCourtAndDisputeKitJumps function consolidates the previously fragmented jump-related methods (earlyCourtJump, getNbVotesAfterAppeal, getJumpDisputeKitID) into a single, comprehensive interface. This reduces complexity and provides all jump-related information in one call.

contracts/deploy/00-home-chain-arbitration-mainnet.ts (2)

34-34: LGTM! Correct alignment with updated initializer signature.

The args array correctly reflects the removal of _jumpDisputeKitID parameter. Using ZeroAddress for core is appropriate here since KlerosCore hasn't been deployed yet; Line 92 updates it via changeCore.


128-128: LGTM! Args correctly updated for new initializer signatures.

Both DisputeKitShutter and DisputeKitGated args arrays correctly reflect the removal of _jumpDisputeKitID and properly use core.target since KlerosCore is already deployed at this point.

Also applies to: 136-136

contracts/src/arbitration/dispute-kits/DisputeKitClassicBase.sol (4)

61-68: Well-designed struct for per-court configuration.

The NextRoundSettings struct provides comprehensive control over next-round behavior with clear field semantics. The separation of earlyDisputeKitJump and earlyCourtJump flags allows fine-grained control over when jumps occur relative to threshold conditions.


85-85: LGTM! Appropriate storage for per-court settings.

The public mapping allows transparent per-court configuration and correctly uses uint96 for court IDs, matching KlerosCore's convention.


161-169: LGTM! Initializer correctly updated to remove global jump dispute kit ID.

The removal of _jumpDisputeKitID aligns with the PR's shift to per-court NextRoundSettings. This change is correctly reflected in the deployment scripts (e.g., Line 34 of 00-home-chain-arbitration-mainnet.ts).


433-434: LGTM! Correct usage of the new API.

The code correctly adapts to the expanded getCourtAndDisputeKitJumps return signature, extracting only the needed isDisputeKitJumping flag while skipping irrelevant return values.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
contracts/src/arbitration/dispute-kits/DisputeKitClassicBase.sol (1)

196-204: Add validation + event for governance changes.

Prevent bad IDs and emit an event for observability.

+    event NextRoundSettingsChanged(uint96 indexed currentCourtID, NextRoundSettings settings);
@@
     function changeNextRoundSettings(
         uint96 _currentCourtID,
         NextRoundSettings memory _nextRoundSettings
     ) external onlyByOwner {
+        if (_nextRoundSettings.enabled) {
+            if (_nextRoundSettings.jumpDisputeKitID != 0) {
+                require(
+                    _nextRoundSettings.jumpDisputeKitID < core.getDisputeKitsLength(),
+                    "Invalid dispute kit ID"
+                );
+            }
+            // Optional: court ID validation requires a Core getter; consider adding one in Core.
+        }
         courtIDToNextRoundSettings[_currentCourtID] = _nextRoundSettings;
+        emit NextRoundSettingsChanged(_currentCourtID, _nextRoundSettings);
     }
contracts/src/arbitration/KlerosCore.sol (1)

1233-1276: Recalculate nbVotes after fallback to Classic to avoid mixing policies.

Current fallback forces (nbVotes*2)+1, which may differ from Classic DK’s configured NextRoundSettings and produce inconsistencies. Recompute via Classic DK.

         if (!courts[newCourtID].supportedDisputeKits[newDisputeKitID] || newDisputeKitID == NULL_DISPUTE_KIT) {
             // Fall back to `DisputeKitClassic` which is always supported.
             newDisputeKitID = DISPUTE_KIT_CLASSIC;
             disputeKitJump = (newDisputeKitID != disputeKitID);
-            newRoundNbVotes = (newRoundNbVotes * 2) + 1; // Reset nbVotes to the default logic.
+            // Recalculate votes using the fallback DK policy.
+            (, , newRoundNbVotes, , ) = disputeKits[DISPUTE_KIT_CLASSIC].getCourtAndDisputeKitJumps(
+                _disputeID,
+                _dispute.courtID,
+                _court.parent,
+                _court.jurorsForCourtJump,
+                DISPUTE_KIT_CLASSIC,
+                _round.nbVotes
+            );
         }
🧹 Nitpick comments (4)
contracts/src/arbitration/interfaces/IDisputeKit.sol (2)

54-58: Document zero-sentinel behavior for fromSubcourtID in draw().

Core normalizes 0 to current dispute.courtID. Clarify in NatSpec that 0 may be returned and what it means to avoid integrator ambiguity.


127-156: Interface param bloat; consider bundling context.

Six inputs and five outputs increase ABI churn risk and stack-pressure. Consider a JumpContext struct for inputs to future‑proof and improve readability, or at least document invariants (e.g., 0 means “no override”) for each field.

contracts/src/arbitration/KlerosCore.sol (1)

788-807: Use DK-provided newRoundNbVotes instead of recomputing from msg.value.

Capture and use the third return from _getCourtAndDisputeKitJumps for extraRound.nbVotes to keep one source of truth (and catch mismatches early).

-        (uint96 newCourtID, uint256 newDisputeKitID, , bool courtJump, ) = _getCourtAndDisputeKitJumps(
+        (uint96 newCourtID, uint256 newDisputeKitID, uint256 newRoundNbVotes, bool courtJump, ) = _getCourtAndDisputeKitJumps(
             dispute,
             round,
             courts[dispute.courtID],
             _disputeID
         );
@@
-        Court storage court = courts[newCourtID];
-        extraRound.nbVotes = msg.value / court.feeForJuror; // As many votes that can be afforded by the provided funds.
+        Court storage court = courts[newCourtID];
+        // Keep nbVotes consistent with DK policy used to compute appealCost.
+        extraRound.nbVotes = newRoundNbVotes;
contracts/src/arbitration/dispute-kits/DisputeKitClassicBase.sol (1)

61-68: Clarify semantics of zero values in NextRoundSettings.

Document that 0 means “no override” for nbVotes/jump IDs, and how early* flags interact. This reduces governance misconfiguration risk.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 973f572 and 6cbd494.

📒 Files selected for processing (3)
  • contracts/src/arbitration/KlerosCore.sol (4 hunks)
  • contracts/src/arbitration/dispute-kits/DisputeKitClassicBase.sol (6 hunks)
  • contracts/src/arbitration/interfaces/IDisputeKit.sol (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Redirect rules - kleros-v2-testnet
  • GitHub Check: Header rules - kleros-v2-testnet
  • GitHub Check: Pages changed - kleros-v2-testnet
🔇 Additional comments (2)
contracts/src/arbitration/KlerosCore.sol (2)

1089-1108: LGTM: appeal cost uses DK-provided nbVotes and switches fee by parent on court jump.


1183-1213: LGTM: public getCourtAndDisputeKitJumps wrapper mirrors internal logic.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (4)
contracts/test/foundry/KlerosCore_Appeals.t.sol (2)

256-268: Good use of per‑court NextRoundSettings; add one test without overrides.

You mention tests should pass without the override. Please add a variant asserting the same jump behavior with default settings to guard regressions in default logic.

Also applies to: 403-415, 526-536, 548-558


298-300: Correct migration to getCourtAndDisputeKitJumps.

Using the new public view to assert DK jumps is appropriate. Consider also asserting the returned newCourtID/newDisputeKitID to fully validate the path.

Also applies to: 448-450, 659-661, 730-732

contracts/src/arbitration/KlerosCore.sol (1)

788-794: Clarify why returned nbVotes is ignored in appeal().

Intent is to derive nbVotes from msg.value/feeForJuror (consistent with appealCost). Add a short comment to prevent future “bug fix” attempts.

-        (uint96 newCourtID, uint256 newDisputeKitID, ) = _getCompatibleNextRoundSettings(
+        // nbVotes from _getCompatibleNextRoundSettings is intentionally ignored:
+        // we recompute nbVotes below from the paid appealCost to keep source-of-truth in Core.
+        (uint96 newCourtID, uint256 newDisputeKitID, ) = _getCompatibleNextRoundSettings(
             dispute,
             round,
             courts[dispute.courtID],
             _disputeID
         );
contracts/src/arbitration/dispute-kits/DisputeKitClassicBase.sol (1)

200-209: Add minimal validation or explicit docs for governance setter.

As-is, owner can set non‑existent court/DK IDs causing Core fallback or worse UX. Either:

  • Add basic sanity checks (preferred), or
  • Document that Core sanitizes/falls back and zero means “unset”.
 function changeNextRoundSettings(
     uint96 _currentCourtID,
     NextRoundSettings memory _nextRoundSettings
 ) external onlyByOwner {
+    // Optional basic sanity: zero means "unset"
+    if (_nextRoundSettings.jumpCourtID != 0) {
+        // Best-effort bound check via Core: treat out-of-range as unset.
+        // If Core exposes a courts length getter in future, use it here.
+    }
+    if (_nextRoundSettings.jumpDisputeKitID == 0) {
+        // ok: unset -> Core keeps current DK unless jumping
+    }
     courtIDToNextRoundSettings[_currentCourtID] = _nextRoundSettings;
     emit NextRoundSettingsChanged(_currentCourtID, _nextRoundSettings);
 }

Based on learnings

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 91ccfe9 and d03832d.

📒 Files selected for processing (3)
  • contracts/src/arbitration/KlerosCore.sol (4 hunks)
  • contracts/src/arbitration/dispute-kits/DisputeKitClassicBase.sol (7 hunks)
  • contracts/test/foundry/KlerosCore_Appeals.t.sol (12 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (14)
  • GitHub Check: Redirect rules - kleros-v2-testnet
  • GitHub Check: Redirect rules - kleros-v2-testnet
  • GitHub Check: Redirect rules - kleros-v2-neo
  • GitHub Check: Header rules - kleros-v2-testnet
  • GitHub Check: Header rules - kleros-v2-testnet
  • GitHub Check: Redirect rules - kleros-v2-testnet-devtools
  • GitHub Check: Header rules - kleros-v2-neo
  • GitHub Check: Header rules - kleros-v2-testnet-devtools
  • GitHub Check: Pages changed - kleros-v2-testnet
  • GitHub Check: Pages changed - kleros-v2-neo
  • GitHub Check: Pages changed - kleros-v2-testnet
  • GitHub Check: Pages changed - kleros-v2-testnet-devtools
  • GitHub Check: hardhat-tests
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (3)
contracts/test/foundry/KlerosCore_Appeals.t.sol (1)

217-223: Initializer arity updates look correct.

All DK proxy init payloads consistently use initialize(address,address,address). No issues spotted.

Also applies to: 357-363, 366-372, 516-523, 539-545

contracts/src/arbitration/dispute-kits/DisputeKitClassicBase.sol (2)

61-67: NextRoundSettings: interface clear.

Fields and zero-as-unset semantics are clear. LGTM.


438-444: Good: query jump before mutating local dispute state.

Consulting Core.getCourtAndDisputeKitJumps to decide whether to create a fresh local round avoids stale state when jumping DKs.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
contracts/src/arbitration/dispute-kits/DisputeKitClassicBase.sol (1)

200-209: Consider adding basic validation to prevent governance errors.

While the event emission addresses part of the previous review feedback, the function still accepts settings without validation. Although Core is responsible for compatibility checking, basic sanity checks here would catch obvious governance mistakes before they're committed to state.

Consider adding validation:

     function changeNextRoundSettings(
         uint96 _currentCourtID,
         NextRoundSettings memory _nextRoundSettings
     ) external onlyByOwner {
+        // Basic validation to catch obvious errors
+        if (_nextRoundSettings.enabled) {
+            if (_nextRoundSettings.jumpCourtID != 0) {
+                require(_nextRoundSettings.jumpCourtID < core.getNumberOfCourts(), "Invalid court ID");
+            }
+            if (_nextRoundSettings.jumpDisputeKitID != 0) {
+                require(core.getNumberOfDisputeKits() > _nextRoundSettings.jumpDisputeKitID, "Invalid DK ID");
+            }
+            if (_nextRoundSettings.jumpDisputeKitIDOnCourtJump != 0) {
+                require(core.getNumberOfDisputeKits() > _nextRoundSettings.jumpDisputeKitIDOnCourtJump, "Invalid fallback DK ID");
+            }
+        }
         courtIDToNextRoundSettings[_currentCourtID] = _nextRoundSettings;
         emit NextRoundSettingsChanged(_currentCourtID, _nextRoundSettings);
     }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d03832d and e2daec5.

📒 Files selected for processing (2)
  • contracts/src/arbitration/dispute-kits/DisputeKitClassicBase.sol (7 hunks)
  • contracts/test/foundry/KlerosCore_Appeals.t.sol (12 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (14)
  • GitHub Check: Redirect rules - kleros-v2-testnet-devtools
  • GitHub Check: Header rules - kleros-v2-testnet-devtools
  • GitHub Check: Pages changed - kleros-v2-testnet-devtools
  • GitHub Check: Redirect rules - kleros-v2-testnet
  • GitHub Check: Redirect rules - kleros-v2-testnet
  • GitHub Check: Header rules - kleros-v2-testnet
  • GitHub Check: Header rules - kleros-v2-testnet
  • GitHub Check: Pages changed - kleros-v2-testnet
  • GitHub Check: Pages changed - kleros-v2-testnet
  • GitHub Check: Redirect rules - kleros-v2-neo
  • GitHub Check: Header rules - kleros-v2-neo
  • GitHub Check: Pages changed - kleros-v2-neo
  • GitHub Check: hardhat-tests
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (5)
contracts/src/arbitration/dispute-kits/DisputeKitClassicBase.sol (2)

636-670: LGTM - Court jump logic correctly fixed.

The implementation correctly checks the current round's vote count (_currentRoundNbVotes >= _currentCourtJurorsForJump at line 654) rather than the projected next round count, addressing the previous review feedback. The override logic is clear: explicit settings take precedence, with sensible defaults applied when values are undefined (0).


438-439: LGTM - API update correctly applied.

The transition from the old isDisputeKitJumping method to the new getCourtAndDisputeKitJumps API is implemented correctly, extracting the isDisputeKitJumping boolean from the returned tuple.

contracts/test/foundry/KlerosCore_Appeals.t.sol (3)

217-222: LGTM - Initialization signatures correctly updated.

All DisputeKit initialization calls have been properly updated to use the new 3-parameter signature (initialize(address,address,address)), removing the deprecated _jumpDisputeKitID parameter. The updates are consistent across all test cases.

Also applies to: 357-371, 517-544


256-267: LGTM - NextRoundSettings properly exercised.

The tests correctly configure various NextRoundSettings scenarios through changeNextRoundSettings, including:

  • Explicit court and DK jumps (lines 256-267)
  • Conditional DK jumps using jumpDisputeKitIDOnCourtJump fallback (lines 403-414, 526-558)

This provides good coverage of the new configuration API.

Also applies to: 403-414, 526-558


298-299: LGTM - Test assertions updated for new API.

All assertions correctly use getCourtAndDisputeKitJumps to check jump conditions, properly extracting the isDisputeKitJumping boolean from the returned tuple. The pattern is consistently applied across all test cases.

Also applies to: 448-449, 659-660, 730-731

sanity check in Core._getCompatibleNextRoundSettings(),
bug fix in appealCost(),
naming of the NextRoundSettingsChanged parameters,
better comments describing the NextRoundSettings parameters.
@jaybuidl jaybuidl marked this pull request as ready for review October 24, 2025 13:56
@jaybuidl jaybuidl linked an issue Oct 24, 2025 that may be closed by this pull request
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
contracts/src/arbitration/KlerosCore.sol (1)

1083-1103: Remove unreachable Forking branch in appealCost.

_getCompatibleNextRoundSettings() already forbids FORKING_COURT by falling back to current court. The NON_PAYABLE_AMOUNT path can no longer be reached; simplify to two branches.

-        if (newCourtID == dispute.courtID) {
-            // No court jump
-            return court.feeForJuror * nbVotesAfterAppeal;
-        }
-        if (dispute.courtID != GENERAL_COURT && newCourtID != FORKING_COURT) {
-            // Court jump but not to the Forking court
-            return courts[newCourtID].feeForJuror * nbVotesAfterAppeal;
-        }
-        return NON_PAYABLE_AMOUNT; // Jumping to the Forking Court is not supported yet.
+        if (newCourtID == dispute.courtID) {
+            return court.feeForJuror * nbVotesAfterAppeal;
+        }
+        return courts[newCourtID].feeForJuror * nbVotesAfterAppeal;
contracts/src/arbitration/dispute-kits/DisputeKitClassicBase.sol (1)

200-209: Add minimal validation in changeNextRoundSettings (defense‑in‑depth).

Core clamps inputs later, but basic checks here prevent obvious misconfigurations by governance.

 function changeNextRoundSettings(
     uint96 _courtID,
     NextRoundSettings memory _nextRoundSettings
 ) external onlyByOwner {
+    if (_nextRoundSettings.enabled) {
+        // 0 means “unspecified”; only validate non-zero overrides.
+        if (_nextRoundSettings.jumpCourtID != 0) {
+            // Ensure not FORKING_COURT and within Core bounds.
+            ( , , , , ) = core.courts(_nextRoundSettings.jumpCourtID); // reverts if OOB
+            require(_nextRoundSettings.jumpCourtID != core.FORKING_COURT(), "Invalid jump court");
+        }
+        if (_nextRoundSettings.jumpDisputeKitID != 0) {
+            require(_nextRoundSettings.jumpDisputeKitID < core.getDisputeKitsLength(), "Invalid DK ID");
+        }
+        if (_nextRoundSettings.jumpDisputeKitIDOnCourtJump != 0) {
+            require(_nextRoundSettings.jumpDisputeKitIDOnCourtJump < core.getDisputeKitsLength(), "Invalid DK ID (on jump)");
+        }
+    }
     courtIDToNextRoundSettings[_courtID] = _nextRoundSettings;
     emit NextRoundSettingsChanged(_courtID, _nextRoundSettings);
 }

If you prefer to avoid extra calls, consider a lighter check (e.g., only DK ID bounds) and rely on Core for court validation.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e2daec5 and bbcc819.

📒 Files selected for processing (2)
  • contracts/src/arbitration/KlerosCore.sol (4 hunks)
  • contracts/src/arbitration/dispute-kits/DisputeKitClassicBase.sol (7 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (14)
  • GitHub Check: Redirect rules - kleros-v2-testnet-devtools
  • GitHub Check: Header rules - kleros-v2-testnet-devtools
  • GitHub Check: Pages changed - kleros-v2-testnet-devtools
  • GitHub Check: Redirect rules - kleros-v2-testnet
  • GitHub Check: Redirect rules - kleros-v2-testnet
  • GitHub Check: Redirect rules - kleros-v2-neo
  • GitHub Check: Header rules - kleros-v2-testnet
  • GitHub Check: Header rules - kleros-v2-testnet
  • GitHub Check: Header rules - kleros-v2-neo
  • GitHub Check: Pages changed - kleros-v2-testnet
  • GitHub Check: Pages changed - kleros-v2-testnet
  • GitHub Check: Pages changed - kleros-v2-neo
  • GitHub Check: hardhat-tests
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (8)
contracts/src/arbitration/KlerosCore.sol (2)

788-799: Appeal flow integration looks correct.

Using the compatibility helper to resolve next-round court/DK, then deriving nbVotes from msg.value/fee is consistent with appealCost and DK fund flow.


1178-1210: Public jump probe API is clear and useful.

Return shape and booleans are intuitive; compatibility helper reuse keeps semantics aligned.

Please ensure off-chain consumers expect newRoundNbVotes to be the post-compatibility, potentially defaulted value (not raw DK output).

contracts/src/arbitration/dispute-kits/DisputeKitClassicBase.sol (6)

61-68: NextRoundSettings shape LGTM.

Fields cover court/DK overrides, court-jump-specific DK, and nbVotes override, all optional.


84-85: Storage mapping is appropriate.

Per‑court settings keyed by current court ID is the right granularity.


132-136: Good: settings change event added.

Enables off‑chain monitoring and audits of governance changes.


165-169: Initializer signature update is clean.

Removal of jump param aligns with per‑court settings; wNative inclusion preserved.


438-439: Correct: use Core jump probe to decide local round creation.

Avoids creating a local round when the dispute kit will jump.


636-670: getNextRoundSettings logic is sound and matches Core semantics.

  • Court jump uses current‑round threshold (>= jurorsForCourtJump).
  • DK override on court jump respects jumpDisputeKitIDOnCourtJump only when jump happens.
  • nbVotes defaults to doubling+1 when unspecified.

Add tests for boundaries: exactly at threshold, nbVotes override with/without enabled, and court jump with/without jumpDisputeKitIDOnCourtJump.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
contracts/test/foundry/KlerosCore_Appeals.t.sol (1)

298-299: Consider asserting additional values from getCourtAndDisputeKitJumps.

While the API change is correct, the test only verifies isDisputeKitJumping and ignores the other return values (nextCourtID, nextDisputeKitID, nextNbVotes, isCourtJumping). Consider adding assertions for these values to strengthen test coverage and validate the complete jump prediction logic.

For example:

-(, , , , bool isDisputeKitJumping) = core.getCourtAndDisputeKitJumps(disputeID);
-assertEq(isDisputeKitJumping, true, "Should be jumping");
+(uint96 nextCourtID, uint256 nextDisputeKitID, , bool isCourtJumping, bool isDisputeKitJumping) = core.getCourtAndDisputeKitJumps(disputeID);
+assertEq(nextCourtID, GENERAL_COURT, "Should jump to GENERAL_COURT");
+assertEq(nextDisputeKitID, DISPUTE_KIT_CLASSIC, "Should jump to DISPUTE_KIT_CLASSIC");
+assertEq(isCourtJumping, true, "Should be court jumping");
+assertEq(isDisputeKitJumping, true, "Should be DK jumping");
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bbcc819 and 7df6109.

📒 Files selected for processing (1)
  • contracts/test/foundry/KlerosCore_Appeals.t.sol (13 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (14)
  • GitHub Check: Redirect rules - kleros-v2-testnet-devtools
  • GitHub Check: Header rules - kleros-v2-testnet-devtools
  • GitHub Check: Redirect rules - kleros-v2-neo
  • GitHub Check: Redirect rules - kleros-v2-testnet
  • GitHub Check: Redirect rules - kleros-v2-testnet
  • GitHub Check: Header rules - kleros-v2-neo
  • GitHub Check: Pages changed - kleros-v2-testnet-devtools
  • GitHub Check: Header rules - kleros-v2-testnet
  • GitHub Check: Header rules - kleros-v2-testnet
  • GitHub Check: Pages changed - kleros-v2-neo
  • GitHub Check: Pages changed - kleros-v2-testnet
  • GitHub Check: Pages changed - kleros-v2-testnet
  • GitHub Check: hardhat-tests
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (9)
contracts/test/foundry/KlerosCore_Appeals.t.sol (9)

217-222: LGTM: Initialization signature correctly updated.

The removal of the jumpDisputeKitID parameter from the initialization call aligns with the PR's refactoring to use the NextRoundSettings mechanism for managing next-round configurations.


403-414: LGTM: NextRoundSettings correctly configured for fallback testing.

This configuration properly tests the scenario where a dispute jumps to a parent court but the current DK is not supported, triggering the jumpDisputeKitIDOnCourtJump fallback to dkID2.


516-558: LGTM: Circular DK jump setup is well-designed.

The test correctly configures a circular dispute kit pattern (DK3 → DK2 → DK3) to validate that disputes can jump back to previously used DKs. The NextRoundSettings configurations properly use jumpDisputeKitIDOnCourtJump to specify the fallback DK when courts jump.


990-1117: Excellent test coverage for non-parent court jumps.

This test comprehensively validates the new NextRoundSettings functionality, specifically:

  • Jumps to non-parent (sibling) courts work correctly
  • appealCost() correctly uses the target court's feeForJuror, not the current court's
  • getCourtAndDisputeKitJumps() accurately predicts the jump

The test structure is clear and assertions are thorough.


1119-1237: LGTM: Multi-level court jump test is comprehensive.

This test validates that disputes can jump multiple levels up the court hierarchy (directly to grandparent), skipping intermediate courts. The test properly verifies both the jump behavior and cost calculations.


1239-1345: LGTM: Disabled NextRoundSettings fallback test is thorough.

This test properly validates that when NextRoundSettings.enabled = false, all custom settings are ignored and the default parent court jump logic is used. The assertions correctly verify that the configured jumpCourtID and nbVotes are not applied.


1440-1669: Excellent coverage of custom nbVotes functionality.

These two tests comprehensively validate the NextRoundSettings.nbVotes feature:

  1. test_appeal_jumpCourtWithCustomNbVotes (lines 1440-1560): Validates that custom nbVotes is correctly used in cost calculations and the new round configuration.

  2. test_appeal_customNbVotesDoesNotTriggerCourtJump (lines 1562-1669): Critically validates that custom nbVotes for the next round does NOT influence whether a court jump occurs (which is based on the current round's drawn jurors). This is an important edge case.

Both tests are well-structured with clear assertions and comments explaining the expected behavior.


1347-1438: Review comment is correct and test behavior is intentional.

The _getCompatibleNextRoundSettings function explicitly validates and handles invalid jumpCourtID values. At lines 1254-1264 in KlerosCore.sol, when newCourtID >= courts.length (which catches the 999 case), the system intentionally falls back to the current court with the comment "Falling back to the current court and dispute kit with default nbVotes." This defensive fallback design is intentional, not accidental. The test correctly validates this behavior.


256-267: Remove the misleading comment about NextRoundSettings override being unnecessary.

The override at lines 256-267 is actually required for the test to pass. Without it, the dispute kit would not jump to DISPUTE_KIT_CLASSIC (it would remain as newDkID), causing the DisputeKitJump event assertion at lines 305-306 to fail. The comment stating "the test should pass even without this override" is incorrect and should be removed or corrected to explain why the override is necessary.

Likely an incorrect or invalid review comment.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
contracts/test/foundry/KlerosCore_Appeals.t.sol (1)

256-268: Clarify the comment about test passing without override.

The comment states "Note that the test should pass even without this override," but then proceeds to configure NextRoundSettings. If the test truly passes without this configuration, consider removing it to avoid confusion, or clarify why it's included despite being technically unnecessary (e.g., for explicitness, future-proofing, or demonstrating the API).

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7df6109 and 0b4cb0c.

📒 Files selected for processing (1)
  • contracts/test/foundry/KlerosCore_Appeals.t.sol (13 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (15)
  • GitHub Check: Redirect rules - kleros-v2-neo
  • GitHub Check: Header rules - kleros-v2-neo
  • GitHub Check: Pages changed - kleros-v2-neo
  • GitHub Check: Redirect rules - kleros-v2-testnet
  • GitHub Check: Redirect rules - kleros-v2-testnet
  • GitHub Check: Header rules - kleros-v2-testnet
  • GitHub Check: Header rules - kleros-v2-testnet
  • GitHub Check: Pages changed - kleros-v2-testnet
  • GitHub Check: Pages changed - kleros-v2-testnet
  • GitHub Check: Redirect rules - kleros-v2-testnet-devtools
  • GitHub Check: Header rules - kleros-v2-testnet-devtools
  • GitHub Check: Pages changed - kleros-v2-testnet-devtools
  • GitHub Check: SonarCloud
  • GitHub Check: hardhat-tests
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (9)
contracts/test/foundry/KlerosCore_Appeals.t.sol (9)

218-221: LGTM: Initialize signature changes align with PR objectives.

The removal of the jumpDisputeKitID parameter from DisputeKit initialization calls is consistent throughout the test file and matches the PR's refactoring to use per-court NextRoundSettings instead.

Also applies to: 358-361, 367-370, 518-521, 540-543, 1683-1686, 1691-1695


298-299: LGTM: Correct migration to new API.

The tests correctly use getCourtAndDisputeKitJumps which returns a comprehensive tuple including both court and DK jump information, replacing the previous separate isDisputeKitJumping function. The destructuring correctly extracts the 5th return value.

Also applies to: 448-449, 659-660, 730-731


991-1117: LGTM: Comprehensive test of non-parent court jumps with cost validation.

This test thoroughly validates:

  • Jumping to a sibling court (not parent) using NextRoundSettings.jumpCourtID
  • appealCost() correctly uses the target court's feeForJuror (0.07 vs 0.05)
  • Correct multiplier application for losing (2x) and winning (1x) sides
  • Jump prediction via getCourtAndDisputeKitJumps

The arithmetic and assertions are correct.


1119-1237: LGTM: Multi-level court jump test validates hierarchy skipping.

This test properly validates that a court can jump multiple levels up the hierarchy (Court4 -> GENERAL_COURT, skipping Court2 and Court3). The cost calculation correctly uses the target court's feeForJuror, and the jump logic works as expected.


1239-1345: LGTM: Fallback behavior correctly tested.

This test validates the critical fallback scenario where NextRoundSettings.enabled = false causes the system to ignore custom settings and use default parent-court jump logic. The assertions correctly verify:

  • Jump to parent (GENERAL_COURT), not custom jumpCourtID (Court3)
  • Default vote formula (7), not custom nbVotes (9)
  • Parent court's feeForJuror in cost calculation

1347-1438: LGTM: Invalid jumpCourtID fallback behavior tested.

This test validates that when NextRoundSettings.jumpCourtID is set to an invalid/non-existent court ID, the system falls back to staying in the current court (no jump). The test correctly verifies:

  • No court jump occurs (isCourtJumping = false)
  • Appeal cost uses current court's feeForJuror
  • New round stays in the same court

1440-1560: LGTM: Custom nbVotes correctly tested.

This test comprehensively validates that NextRoundSettings.nbVotes overrides the default formula (currentVotes * 2 + 1). The test correctly verifies:

  • appealCost() uses custom nbVotes (11) with target court's fee (0.08 * 11 = 0.88)
  • Multipliers correctly applied (2.64 for loser, 1.76 for winner)
  • New round created with custom nbVotes (11), not default (7)
  • All jurors can be drawn with custom count

1562-1669: LGTM: Critical test validates correct jump trigger logic.

This is an important test that validates a subtle but critical behavior: court jump decisions are based on the current round's drawn jurors (3), not the next round's custom nbVotes (9). Despite customNbVotes = 9 > jurorsForCourtJump = 7, no court jump occurs because the current round only has 3 jurors. This ensures the jump threshold is evaluated against actual dispute scale, not future projections.


1671-1838: LGTM: Precedence rule correctly validated.

This test validates the important precedence rule: when both jumpDisputeKitID and jumpDisputeKitIDOnCourtJump are set, jumpDisputeKitID takes precedence. The test comprehensively verifies:

  • getCourtAndDisputeKitJumps predicts dkID2 (not dkID3)
  • DisputeKitJump event shows jump to dkID2
  • New round uses DisputeKit2, not DisputeKit3
  • Correct dispute kit activation states

This precedence rule avoids configuration ambiguity and is well-tested.

coderabbitai[bot]
coderabbitai bot previously approved these changes Oct 24, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
contracts/src/arbitration/KlerosCore.sol (1)

788-796: Consider documenting why newRoundNbVotes is discarded.

The third return value from _getCompatibleNextRoundSettings is intentionally discarded on line 788 because nbVotes is recalculated on line 803 based on msg.value / court.feeForJuror. This design allows appellants to pay for more votes than the minimum cost, but the reasoning is not immediately obvious from the code.

Consider adding a brief comment to clarify the design:

-        (uint96 newCourtID, uint256 newDisputeKitID, ) = _getCompatibleNextRoundSettings(
+        // Note: newRoundNbVotes is discarded here as the actual nbVotes is calculated from msg.value on line 803
+        (uint96 newCourtID, uint256 newDisputeKitID, ) = _getCompatibleNextRoundSettings(
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0b4cb0c and 8704a3c.

📒 Files selected for processing (1)
  • contracts/src/arbitration/KlerosCore.sol (4 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (14)
  • GitHub Check: Redirect rules - kleros-v2-neo
  • GitHub Check: Header rules - kleros-v2-neo
  • GitHub Check: Pages changed - kleros-v2-neo
  • GitHub Check: Redirect rules - kleros-v2-testnet-devtools
  • GitHub Check: Redirect rules - kleros-v2-testnet
  • GitHub Check: Redirect rules - kleros-v2-testnet
  • GitHub Check: Header rules - kleros-v2-testnet-devtools
  • GitHub Check: Header rules - kleros-v2-testnet
  • GitHub Check: Header rules - kleros-v2-testnet
  • GitHub Check: Pages changed - kleros-v2-testnet-devtools
  • GitHub Check: Pages changed - kleros-v2-testnet
  • GitHub Check: Pages changed - kleros-v2-testnet
  • GitHub Check: hardhat-tests
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (2)
contracts/src/arbitration/KlerosCore.sol (2)

1178-1210: LGTM! Clean implementation of the new public API.

The new getCourtAndDisputeKitJumps function provides clear visibility into next-round settings and jump indicators. The implementation correctly delegates to _getCompatibleNextRoundSettings and properly computes the boolean jump flags.


1230-1272: Excellent defensive validation - all past concerns addressed.

The _getCompatibleNextRoundSettings implementation includes comprehensive guards:

  • Line 1255-1256: Prevents invalid court IDs (FORKING_COURT, out-of-bounds)
  • Line 1257-1258: Prevents invalid dispute kit IDs (NULL_DISPUTE_KIT, out-of-bounds)
  • Line 1259: Prevents zero nbVotes that would enable free appeals (addresses critical past comment)
  • Lines 1267-1271: Ensures court/DK compatibility with fallback to DISPUTE_KIT_CLASSIC

The two-stage fallback strategy (invalid inputs → current court/DK, then incompatibility → DISPUTE_KIT_CLASSIC) provides robust protection against malicious or buggy dispute kit implementations.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
contracts/test/foundry/KlerosCore_Appeals.t.sol (3)

257-268: Consider removing the redundant NextRoundSettings override.

The comment at line 257 indicates "the test should pass even without this override," suggesting this configuration may be unnecessary. If the default behavior already provides the correct jump behavior (court2 → GENERAL_COURT, dk2 → DISPUTE_KIT_CLASSIC), consider removing this override to simplify the test and rely on the default logic.


299-300: Consider validating all getCourtAndDisputeKitJumps return values.

The test retrieves 5 values from getCourtAndDisputeKitJumps but only asserts on isDisputeKitJumping. For more comprehensive validation, consider also asserting the predicted newCourtID, newDisputeKitID, and newRoundNbVotes values, similar to what's done in the new tests starting at line 1084.

Alternatively, if the other values aren't relevant for this test, use a simpler destructuring pattern:

(, , , , bool isDisputeKitJumping) = core.getCourtAndDisputeKitJumps(disputeID);

991-2563: Consider extracting helper functions to reduce test code duplication.

The new test functions contain significant duplication in setup code (court creation, dispute kit initialization, staking, dispute progression). Extracting helper functions would improve maintainability and reduce the risk of inconsistencies:

function _createCourtWithFee(uint96 parent, uint256 fee, uint256 jurorsForJump, uint256[] memory supportedDKs) internal returns (uint96)
function _createAndInitializeDisputeKit() internal returns (DisputeKitClassic, uint256)  
function _setupStakingAndDispute(uint96 courtID, uint256 dkID, uint256 fee) internal returns (uint256)
function _progressToAppealPeriod(uint256 disputeID, address voter, uint256[] memory voteIDs, uint256 choice) internal

This would make the tests more concise and easier to maintain while keeping the unique test logic clearly visible.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8704a3c and ad1ff0d.

📒 Files selected for processing (2)
  • contracts/src/test/DisputeKitClassicMockUncheckedNextRoundSettings.sol (1 hunks)
  • contracts/test/foundry/KlerosCore_Appeals.t.sol (14 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (15)
  • GitHub Check: Redirect rules - kleros-v2-testnet
  • GitHub Check: Redirect rules - kleros-v2-testnet
  • GitHub Check: Redirect rules - kleros-v2-testnet-devtools
  • GitHub Check: Header rules - kleros-v2-testnet
  • GitHub Check: Redirect rules - kleros-v2-neo
  • GitHub Check: Header rules - kleros-v2-testnet
  • GitHub Check: Header rules - kleros-v2-testnet-devtools
  • GitHub Check: Header rules - kleros-v2-neo
  • GitHub Check: Pages changed - kleros-v2-testnet
  • GitHub Check: Pages changed - kleros-v2-testnet
  • GitHub Check: Pages changed - kleros-v2-testnet-devtools
  • GitHub Check: Pages changed - kleros-v2-neo
  • GitHub Check: SonarCloud
  • GitHub Check: hardhat-tests
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (3)
contracts/src/test/DisputeKitClassicMockUncheckedNextRoundSettings.sol (1)

7-22: LGTM! Well-designed test mock for fallback logic validation.

This mock correctly bypasses DisputeKitClassicBase's safety logic to enable testing of KlerosCore's compatibility checks and fallback behavior. The intentional lack of validation allows you to test edge cases like invalid court IDs, invalid dispute kit IDs, and zero vote counts that would normally be caught earlier.

contracts/test/foundry/KlerosCore_Appeals.t.sol (2)

1841-1986: Excellent test coverage for invalid dispute kit ID fallback behavior.

This test thoroughly validates the complete triple fallback mechanism when jumpDisputeKitID >= disputeKits.length. The critical insight is that even though jumpCourtID is valid and nbVotes is custom, all three parameters fall back to current settings due to the single invalid parameter. The assertions comprehensively verify this behavior, and the test documentation clearly explains the safety check being tested.


2161-2563: Excellent test strategy for validating KlerosCore's sanity checks.

These three tests use the mock dispute kit to bypass DisputeKitClassicBase's safety logic and verify that KlerosCore has its own defensive checks. This demonstrates good defense-in-depth:

  1. Line 2161: Tests jumpCourtID == FORKING_COURT (0) triggers fallback
  2. Line 2293: Tests jumpDisputeKitID == NULL_DISPUTE_KIT (0) triggers fallback
  3. Line 2430: Tests newRoundNbVotes == 0 triggers fallback

All three scenarios correctly validate complete triple fallback even when other parameters appear valid. This ensures that even if a malicious or buggy dispute kit returns invalid settings, KlerosCore won't accept them.

@sonarqubecloud
Copy link

Quality Gate Failed Quality Gate failed

Failed conditions
16.7% Duplication on New Code (required ≤ 10%)

See analysis details on SonarQube Cloud

@jaybuidl jaybuidl merged commit b22899c into dev Oct 24, 2025
14 of 20 checks passed
@jaybuidl jaybuidl deleted the feat/next-round-settings-in-dk branch October 24, 2025 22:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Compatibility: ABI change 🗯 Smart contract ABI is changing. Package: Contracts Court smart contracts Type: Feature🗿

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Increased Court and DK jump logic flexibility

2 participants