Skip to content

Commit a2b5d76

Browse files
feat(SortitionModule): add certora PoC to Foundry
1 parent 1006d82 commit a2b5d76

File tree

1 file changed

+104
-0
lines changed

1 file changed

+104
-0
lines changed

contracts/test/foundry/KlerosCore_Execution.t.sol

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {DisputeKitClassicBase} from "../../src/arbitration/dispute-kits/DisputeK
88
import {IArbitratorV2, IArbitrableV2} from "../../src/arbitration/KlerosCore.sol";
99
import {IERC20} from "../../src/libraries/SafeERC20.sol";
1010
import "../../src/libraries/Constants.sol";
11+
import {console} from "forge-std/console.sol";
1112

1213
/// @title KlerosCore_ExecutionTest
1314
/// @dev Tests for KlerosCore execution, rewards, and ruling finalization
@@ -749,6 +750,52 @@ contract KlerosCore_ExecutionTest is KlerosCore_TestBase {
749750
assertEq(address(disputeKit).balance, 0, "Wrong balance of the DK");
750751
}
751752

753+
function test_inflatedTotalStaked_whenDelayedStakeExecute_whenJurorHasNoFunds() public {
754+
// pre conditions
755+
// 1. there is a dispute in drawing phase
756+
// 2. juror call setStake with an amount greater than his PNK balance
757+
// 3. draw jurors, move to voting phase and execute voting
758+
// 4. move sortition to staking phase
759+
uint256 disputeID = 0;
760+
uint256 amountToStake = 20000;
761+
_stakePnk_createDispute_moveToDrawingPhase(disputeID, staker1, amountToStake);
762+
763+
KlerosCore.Round memory round = core.getRoundInfo(disputeID, 0);
764+
uint256 pnkAtStakePerJuror = round.pnkAtStakePerJuror;
765+
_stakeBalanceForJuror(staker1, type(uint256).max);
766+
_drawJurors_advancePeriodToVoting(disputeID);
767+
_vote_execute(disputeID, staker1);
768+
sortitionModule.passPhase(); // set it to staking phase
769+
_assertJurorBalance(
770+
disputeID,
771+
staker1,
772+
amountToStake,
773+
pnkAtStakePerJuror * DEFAULT_NB_OF_JURORS,
774+
amountToStake,
775+
1
776+
);
777+
778+
console.log("totalStaked before: %e", sortitionModule.totalStaked());
779+
780+
// execution: execute delayed stake
781+
sortitionModule.executeDelayedStakes(1);
782+
783+
// post condition: inflated totalStaked
784+
console.log("totalStaked after: %e", sortitionModule.totalStaked());
785+
_assertJurorBalance(
786+
disputeID,
787+
staker1,
788+
amountToStake,
789+
pnkAtStakePerJuror * DEFAULT_NB_OF_JURORS,
790+
amountToStake,
791+
1
792+
);
793+
794+
// new juror tries to stake but totalStaked already reached type(uint256).max
795+
// it reverts with "arithmetic underflow or overflow (0x11)"
796+
_stakeBalanceForJuror(staker2, 20000);
797+
}
798+
752799
function testFuzz_executeIterations(uint256 iterations) public {
753800
uint256 disputeID = 0;
754801
uint256 roundID = 0;
@@ -847,4 +894,61 @@ contract KlerosCore_ExecutionTest is KlerosCore_TestBase {
847894
assertEq(totalLocked, (pnkAtStake * nbJurors) - unlockedTokens, "Wrong amount locked");
848895
assertEq(stakedInCourt, 2000, "Wrong amount staked in court");
849896
}
897+
898+
///////// Internal //////////
899+
900+
function _assertJurorBalance(
901+
uint256 disputeID,
902+
address juror,
903+
uint256 totalStakedPnk,
904+
uint256 totalLocked,
905+
uint256 stakedInCourt,
906+
uint256 nbCourts
907+
) internal {
908+
(uint256 totalStakedPnk, uint256 totalLocked, uint256 stakedInCourt, uint256 nbCourts) = sortitionModule
909+
.getJurorBalance(juror, GENERAL_COURT);
910+
assertEq(totalStakedPnk, totalStakedPnk, "Wrong totalStakedPnk"); // jurors total staked a.k.a juror.stakedPnk
911+
assertEq(totalLocked, totalLocked, "Wrong totalLocked");
912+
assertEq(stakedInCourt, stakedInCourt, "Wrong stakedInCourt"); // juror staked in court a.k.a _stakeOf
913+
assertEq(nbCourts, nbCourts, "Wrong nbCourts");
914+
}
915+
916+
function _stakeBalanceForJuror(address juror, uint256 amount) internal {
917+
console.log("actual juror PNK balance before staking: %e", pinakion.balanceOf(juror));
918+
vm.prank(juror);
919+
core.setStake(GENERAL_COURT, amount);
920+
}
921+
922+
function _stakePnk_createDispute_moveToDrawingPhase(uint256 disputeID, address juror, uint256 amount) internal {
923+
vm.prank(juror);
924+
core.setStake(GENERAL_COURT, amount);
925+
vm.prank(disputer);
926+
arbitrable.createDispute{value: feeForJuror * DEFAULT_NB_OF_JURORS}("Action");
927+
vm.warp(block.timestamp + minStakingTime);
928+
sortitionModule.passPhase(); // Generating
929+
vm.warp(block.timestamp + rngLookahead);
930+
sortitionModule.passPhase(); // Drawing phase
931+
932+
assertEq(sortitionModule.totalStaked(), amount, "!totalStaked");
933+
}
934+
935+
function _drawJurors_advancePeriodToVoting(uint256 disputeID) internal {
936+
core.draw(disputeID, DEFAULT_NB_OF_JURORS);
937+
vm.warp(block.timestamp + timesPerPeriod[0]);
938+
core.passPeriod(disputeID); // Vote
939+
}
940+
941+
function _vote_execute(uint256 disputeID, address juror) internal {
942+
uint256[] memory voteIDs = new uint256[](3);
943+
voteIDs[0] = 0;
944+
voteIDs[1] = 1;
945+
voteIDs[2] = 2;
946+
947+
vm.prank(juror);
948+
disputeKit.castVote(disputeID, voteIDs, 2, 0, "XYZ");
949+
core.passPeriod(disputeID); // Appeal
950+
951+
vm.warp(block.timestamp + timesPerPeriod[3]);
952+
core.passPeriod(disputeID); // Execution
953+
}
850954
}

0 commit comments

Comments
 (0)