From 9cf27d5702c450bb737c3ee0677bb0250c577208 Mon Sep 17 00:00:00 2001 From: Lasse Herskind <16536249+LHerskind@users.noreply.github.com> Date: Fri, 11 Oct 2024 11:50:49 +0100 Subject: [PATCH] test: boundary test --- l1-contracts/src/governance/Gerousia.sol | 2 +- .../src/governance/libraries/Errors.sol | 2 +- .../governance/apella/finaliseWithdraw.t.sol | 19 +++++- .../governance/apella/getProposalState.t.sol | 25 ++++++-- .../governance/gerousia/pushProposal.t.sol | 63 ++++++++++++++----- 5 files changed, 85 insertions(+), 26 deletions(-) diff --git a/l1-contracts/src/governance/Gerousia.sol b/l1-contracts/src/governance/Gerousia.sol index 86f80c1220a..d6a12a10160 100644 --- a/l1-contracts/src/governance/Gerousia.sol +++ b/l1-contracts/src/governance/Gerousia.sol @@ -108,7 +108,7 @@ contract Gerousia is IGerousia { require(_roundNumber < currentRound, Errors.Gerousia__CanOnlyPushProposalInPast()); require( _roundNumber + LIFETIME_IN_ROUNDS >= currentRound, - Errors.Gerousia__ProposalTooOld(_roundNumber) + Errors.Gerousia__ProposalTooOld(_roundNumber, currentRound) ); RoundAccounting storage round = rounds[instance][_roundNumber]; diff --git a/l1-contracts/src/governance/libraries/Errors.sol b/l1-contracts/src/governance/libraries/Errors.sol index cb06f44342b..df3ebc1cb55 100644 --- a/l1-contracts/src/governance/libraries/Errors.sol +++ b/l1-contracts/src/governance/libraries/Errors.sol @@ -54,7 +54,7 @@ library Errors { error Gerousia__ProposalAlreadyExecuted(uint256 roundNumber); // 0x7aeacb17 error Gerousia__ProposalCannotBeAddressZero(); // 0xdb3e4b6e error Gerousia__ProposalHaveNoCode(IPayload proposal); // 0xdce0615b - error Gerousia__ProposalTooOld(uint256 roundNumber); //0x02283b1a + error Gerousia__ProposalTooOld(uint256 roundNumber, uint256 currentRoundNumber); //0x02283b1a error Gerousia__VoteAlreadyCastForSlot(Slot slot); //0xc2201452 error Nomismatokopio__InssuficientMintAvailable(uint256 available, uint256 needed); // 0xf268b931 diff --git a/l1-contracts/test/governance/apella/finaliseWithdraw.t.sol b/l1-contracts/test/governance/apella/finaliseWithdraw.t.sol index 1a7e349c110..f4ae280fee3 100644 --- a/l1-contracts/test/governance/apella/finaliseWithdraw.t.sol +++ b/l1-contracts/test/governance/apella/finaliseWithdraw.t.sol @@ -86,7 +86,8 @@ contract FinaliseWithdrawTest is ApellaBase { uint256 _depositAmount, address[WITHDRAWAL_COUNT] memory _recipient, uint256[WITHDRAWAL_COUNT] memory _withdrawals, - uint256[WITHDRAWAL_COUNT] memory _timejumps + uint256[WITHDRAWAL_COUNT] memory _timejumps, + uint256[WITHDRAWAL_COUNT] memory _timejumps2 ) external whenItMatchPendingWithdrawal(_depositAmount, _recipient, _withdrawals, _timejumps) @@ -99,6 +100,11 @@ contract FinaliseWithdrawTest is ApellaBase { DataStructures.Withdrawal memory withdrawal = apella.getWithdrawal(i); assertGt(withdrawal.unlocksAt, block.timestamp); + uint256 time = + bound(_timejumps2[i], block.timestamp, Timestamp.unwrap(withdrawal.unlocksAt) - 1); + + vm.warp(time); + vm.expectRevert( abi.encodeWithSelector( Errors.Apella__WithdrawalNotUnlockedYet.selector, @@ -114,7 +120,8 @@ contract FinaliseWithdrawTest is ApellaBase { uint256 _depositAmount, address[WITHDRAWAL_COUNT] memory _recipient, uint256[WITHDRAWAL_COUNT] memory _withdrawals, - uint256[WITHDRAWAL_COUNT] memory _timejumps + uint256[WITHDRAWAL_COUNT] memory _timejumps, + uint256[WITHDRAWAL_COUNT] memory _timejumps2 ) external whenItMatchPendingWithdrawal(_depositAmount, _recipient, _withdrawals, _timejumps) @@ -129,7 +136,13 @@ contract FinaliseWithdrawTest is ApellaBase { uint256 withdrawalCount = apella.withdrawalCount(); for (uint256 i = 0; i < withdrawalCount; i++) { DataStructures.Withdrawal memory withdrawal = apella.getWithdrawal(i); - vm.warp(Timestamp.unwrap(withdrawal.unlocksAt)); + + uint256 upper = i + 1 == withdrawalCount + ? type(uint256).max + : Timestamp.unwrap(apella.getWithdrawal(i + 1).unlocksAt); + uint256 time = bound(_timejumps2[i], Timestamp.unwrap(withdrawal.unlocksAt), upper); + + vm.warp(time); vm.expectEmit(true, true, true, true, address(apella)); emit IApella.WithdrawFinalised(i); diff --git a/l1-contracts/test/governance/apella/getProposalState.t.sol b/l1-contracts/test/governance/apella/getProposalState.t.sol index 7c4b2bd83b7..75c15a63c38 100644 --- a/l1-contracts/test/governance/apella/getProposalState.t.sol +++ b/l1-contracts/test/governance/apella/getProposalState.t.sol @@ -79,7 +79,7 @@ contract GetProposalStateTest is ApellaBase { _; } - function test_WhenVotingDelayHaveNotPassed() + function test_WhenVotingDelayHaveNotPassed(uint256 _timeJump) external whenValidProposalId givenStateIsUnstable @@ -87,6 +87,10 @@ contract GetProposalStateTest is ApellaBase { { // it return Pending _statePending("empty"); + + uint256 time = bound(_timeJump, block.timestamp, Timestamp.unwrap(proposal.pendingThrough())); + vm.warp(time); + assertEq(apella.getProposalState(proposalId), DataStructures.ProposalState.Pending); } @@ -94,7 +98,7 @@ contract GetProposalStateTest is ApellaBase { _; } - function test_WhenVotingDurationHaveNotPassed() + function test_WhenVotingDurationHaveNotPassed(uint256 _timeJump) external whenValidProposalId givenStateIsUnstable @@ -103,6 +107,10 @@ contract GetProposalStateTest is ApellaBase { { // it return Active _stateActive("empty"); + + uint256 time = bound(_timeJump, block.timestamp, Timestamp.unwrap(proposal.activeThrough())); + vm.warp(time); + assertEq(apella.getProposalState(proposalId), DataStructures.ProposalState.Active); } @@ -141,7 +149,6 @@ contract GetProposalStateTest is ApellaBase { whenVotingDurationHavePassed { // it return Rejected - _stateQueued("empty", _voter, _totalPower, _votesCast, _yeas); // We can overwrite the quorum to be 0 to hit an invalid case @@ -173,7 +180,8 @@ contract GetProposalStateTest is ApellaBase { address _voter, uint256 _totalPower, uint256 _votesCast, - uint256 _yeas + uint256 _yeas, + uint256 _timeJump ) external whenValidProposalId @@ -184,6 +192,9 @@ contract GetProposalStateTest is ApellaBase { givenVoteTabulationIsAccepted(_voter, _totalPower, _votesCast, _yeas) { // it return Queued + uint256 time = bound(_timeJump, block.timestamp, Timestamp.unwrap(proposal.queuedThrough())); + vm.warp(time); + assertEq(apella.getProposalState(proposalId), DataStructures.ProposalState.Queued); } @@ -196,7 +207,8 @@ contract GetProposalStateTest is ApellaBase { address _voter, uint256 _totalPower, uint256 _votesCast, - uint256 _yeas + uint256 _yeas, + uint256 _timeJump ) external whenValidProposalId @@ -208,6 +220,9 @@ contract GetProposalStateTest is ApellaBase { givenExecutionDelayHavePassed { // it return Executable + uint256 time = bound(_timeJump, block.timestamp, Timestamp.unwrap(proposal.executableThrough())); + vm.warp(time); + assertEq(apella.getProposalState(proposalId), DataStructures.ProposalState.Executable); } diff --git a/l1-contracts/test/governance/gerousia/pushProposal.t.sol b/l1-contracts/test/governance/gerousia/pushProposal.t.sol index 3848885b990..42b97d4cd4e 100644 --- a/l1-contracts/test/governance/gerousia/pushProposal.t.sol +++ b/l1-contracts/test/governance/gerousia/pushProposal.t.sol @@ -47,16 +47,31 @@ contract PushProposalTest is GerousiaBase { _; } - function test_WhenRoundTooFarInPast() external givenCanonicalInstanceHoldCode whenRoundInPast { + function test_WhenRoundTooFarInPast(uint256 _slotsToJump) + external + givenCanonicalInstanceHoldCode + whenRoundInPast + { // it revert - vm.warp( - Timestamp.unwrap( - leonidas.getTimestampForSlot(Slot.wrap((gerousia.LIFETIME_IN_ROUNDS() + 1) * gerousia.M())) + uint256 lower = Timestamp.unwrap( + leonidas.getTimestampForSlot( + leonidas.getCurrentSlot() + Slot.wrap(gerousia.M() * gerousia.LIFETIME_IN_ROUNDS() + 1) ) ); + uint256 upper = + (type(uint256).max - Timestamp.unwrap(leonidas.GENESIS_TIME())) / leonidas.SLOT_DURATION(); + uint256 time = bound(_slotsToJump, lower, upper); - vm.expectRevert(abi.encodeWithSelector(Errors.Gerousia__ProposalTooOld.selector, 0)); + vm.warp(time); + + vm.expectRevert( + abi.encodeWithSelector( + Errors.Gerousia__ProposalTooOld.selector, + 0, + gerousia.computeRound(leonidas.getCurrentSlot()) + ) + ); gerousia.pushProposal(0); } @@ -97,7 +112,7 @@ contract PushProposalTest is GerousiaBase { _; } - function test_GivenLeaderIsAddress0() + function test_GivenLeaderIsAddress0(uint256 _slotsToJump) external givenCanonicalInstanceHoldCode whenRoundInPast @@ -105,6 +120,20 @@ contract PushProposalTest is GerousiaBase { givenRoundNotExecutedBefore { // it revert + + // The first slot in the next round (round 1) + Slot lowerSlot = Slot.wrap(gerousia.M()); + uint256 lower = Timestamp.unwrap(leonidas.getTimestampForSlot(lowerSlot)); + // the last slot in the LIFETIME_IN_ROUNDS next round + uint256 upper = Timestamp.unwrap( + leonidas.getTimestampForSlot( + lowerSlot + Slot.wrap(gerousia.M() * (gerousia.LIFETIME_IN_ROUNDS() - 1)) + ) + ); + uint256 time = bound(_slotsToJump, lower, upper); + + vm.warp(time); + vm.expectRevert(abi.encodeWithSelector(Errors.Gerousia__ProposalCannotBeAddressZero.selector)); gerousia.pushProposal(0); } @@ -135,8 +164,10 @@ contract PushProposalTest is GerousiaBase { gerousia.pushProposal(1); } - modifier givenSufficientYea() { - for (uint256 i = 0; i < gerousia.N(); i++) { + modifier givenSufficientYea(uint256 _yeas) { + uint256 limit = bound(_yeas, gerousia.N(), gerousia.M()); + + for (uint256 i = 0; i < limit; i++) { vm.prank(proposer); assertTrue(gerousia.vote(proposal)); vm.warp( @@ -152,14 +183,14 @@ contract PushProposalTest is GerousiaBase { _; } - function test_GivenNewCanonicalInstance() + function test_GivenNewCanonicalInstance(uint256 _yeas) external givenCanonicalInstanceHoldCode whenRoundInPast whenRoundInRecentPast givenRoundNotExecutedBefore givenLeaderIsNotAddress0 - givenSufficientYea + givenSufficientYea(_yeas) { // it revert @@ -188,14 +219,14 @@ contract PushProposalTest is GerousiaBase { gerousia.pushProposal(1); } - function test_GivenApellaCallReturnFalse() + function test_GivenApellaCallReturnFalse(uint256 _yeas) external givenCanonicalInstanceHoldCode whenRoundInPast whenRoundInRecentPast givenRoundNotExecutedBefore givenLeaderIsNotAddress0 - givenSufficientYea + givenSufficientYea(_yeas) { // it revert FalsyApella falsy = new FalsyApella(); @@ -205,14 +236,14 @@ contract PushProposalTest is GerousiaBase { gerousia.pushProposal(1); } - function test_GivenApellaCallFails() + function test_GivenApellaCallFails(uint256 _yeas) external givenCanonicalInstanceHoldCode whenRoundInPast whenRoundInRecentPast givenRoundNotExecutedBefore givenLeaderIsNotAddress0 - givenSufficientYea + givenSufficientYea(_yeas) { // it revert FaultyApella faulty = new FaultyApella(); @@ -222,14 +253,14 @@ contract PushProposalTest is GerousiaBase { gerousia.pushProposal(1); } - function test_GivenApellaCallSucceeds() + function test_GivenApellaCallSucceeds(uint256 _yeas) external givenCanonicalInstanceHoldCode whenRoundInPast whenRoundInRecentPast givenRoundNotExecutedBefore givenLeaderIsNotAddress0 - givenSufficientYea + givenSufficientYea(_yeas) { // it update executed to true // it emits {ProposalPushed} event