diff --git a/docs/src/content/modules/dispute/bond_escalation_module.md b/docs/src/content/modules/dispute/bond_escalation_module.md index d2bccb86..28c553bf 100644 --- a/docs/src/content/modules/dispute/bond_escalation_module.md +++ b/docs/src/content/modules/dispute/bond_escalation_module.md @@ -24,7 +24,7 @@ The Bond Escalation Module is a contract that allows users to have the first dis - `bondToken`: The address of the token associated with the given request. - `bondSize`: The amount to bond to dispute or propose an answer for the given request. - `maxNumberOfEscalations`: The maximum allowed escalations or pledges for each side during the bond escalation process. -- `bondEscalationDeadline`: The timestamp at which bond escalation process finishes when pledges are not tied. +- `bondEscalationDeadline`: The number of seconds after dispute creation required to finish the bond escalation process when pledges are not tied. - `tyingBuffer`: The number of seconds to extend the bond escalation process to allow the losing party to tie if at the end of the initial deadline the pledges weren't tied. - `disputeWindow`: The number of seconds disputers have to challenge the proposed response since its creation. diff --git a/docs/src/content/modules/response/bonded_response_module.md b/docs/src/content/modules/response/bonded_response_module.md index fd313859..380a7f5d 100644 --- a/docs/src/content/modules/response/bonded_response_module.md +++ b/docs/src/content/modules/response/bonded_response_module.md @@ -20,7 +20,7 @@ The Bonded Response Module is a contract that allows users to propose a response - `accountingExtension`: The address holding the bonded tokens. It must implement the [IAccountingExtension.sol](/solidity/interfaces/extensions/IAccountingExtension.sol/interface.IAccountingExtension.md) interface. - `bondToken`: The ERC20 token used for bonding. - `bondSize`: The amount of tokens the disputer must bond to be able to dispute a response. -- `deadline`: The timestamp at which the module stops accepting new responses for a request and it becomes finalizable. +- `deadline`: The number of seconds after request creation at which the module stops accepting new responses for a request and it becomes finalizable. ## 3. Key Mechanisms & Concepts diff --git a/solidity/contracts/modules/dispute/BondEscalationModule.sol b/solidity/contracts/modules/dispute/BondEscalationModule.sol index 48fff3ca..9f32ea78 100644 --- a/solidity/contracts/modules/dispute/BondEscalationModule.sol +++ b/solidity/contracts/modules/dispute/BondEscalationModule.sol @@ -57,7 +57,9 @@ contract BondEscalationModule is Module, IBondEscalationModule { // Only the first dispute of a request should go through the bond escalation // Consecutive disputes should be handled by the resolution module if (_escalation.status == BondEscalationStatus.None) { - if (block.timestamp > _params.bondEscalationDeadline) revert BondEscalationModule_BondEscalationOver(); + if (block.timestamp > ORACLE.disputeCreatedAt(_disputeId) + _params.bondEscalationDeadline) { + revert BondEscalationModule_BondEscalationOver(); + } _escalation.status = BondEscalationStatus.Active; _escalation.disputeId = _disputeId; emit BondEscalationStatusUpdated(_dispute.requestId, _disputeId, BondEscalationStatus.Active); @@ -83,7 +85,9 @@ contract BondEscalationModule is Module, IBondEscalationModule { if (_disputeStatus == IOracle.DisputeStatus.Escalated) { // The dispute has been escalated to the Resolution module // Make sure the bond escalation deadline has passed and update the status - if (block.timestamp <= _params.bondEscalationDeadline) revert BondEscalationModule_BondEscalationNotOver(); + if (block.timestamp <= ORACLE.disputeCreatedAt(_disputeId) + _params.bondEscalationDeadline) { + revert BondEscalationModule_BondEscalationNotOver(); + } if ( _escalation.status != BondEscalationStatus.Active @@ -252,7 +256,8 @@ contract BondEscalationModule is Module, IBondEscalationModule { RequestParameters memory _params = decodeRequestData(_request.disputeModuleData); BondEscalation storage _escalation = _escalations[_dispute.requestId]; - if (block.timestamp <= _params.bondEscalationDeadline + _params.tyingBuffer) { + uint256 _disputeCreatedAt = ORACLE.disputeCreatedAt(_disputeId); + if (block.timestamp <= _disputeCreatedAt + _params.bondEscalationDeadline + _params.tyingBuffer) { revert BondEscalationModule_BondEscalationNotOver(); } @@ -290,6 +295,7 @@ contract BondEscalationModule is Module, IBondEscalationModule { bool _forDispute ) internal view returns (RequestParameters memory _params) { bytes32 _disputeId = _validateDispute(_request, _dispute); + BondEscalation memory _escalation = _escalations[_dispute.requestId]; if (_disputeId != _escalation.disputeId) { @@ -298,7 +304,8 @@ contract BondEscalationModule is Module, IBondEscalationModule { _params = decodeRequestData(_request.disputeModuleData); - if (block.timestamp > _params.bondEscalationDeadline + _params.tyingBuffer) { + uint256 _disputeCreatedAt = ORACLE.disputeCreatedAt(_disputeId); + if (block.timestamp > _disputeCreatedAt + _params.bondEscalationDeadline + _params.tyingBuffer) { revert BondEscalationModule_BondEscalationOver(); } @@ -317,7 +324,10 @@ contract BondEscalationModule is Module, IBondEscalationModule { if (_numPledgersAgainstDispute > _numPledgersForDispute) revert BondEscalationModule_CanOnlySurpassByOnePledge(); } - if (block.timestamp > _params.bondEscalationDeadline && _numPledgersForDispute == _numPledgersAgainstDispute) { + if ( + block.timestamp > _disputeCreatedAt + _params.bondEscalationDeadline + && _numPledgersForDispute == _numPledgersAgainstDispute + ) { revert BondEscalationModule_CannotBreakTieDuringTyingBuffer(); } } diff --git a/solidity/contracts/modules/response/BondedResponseModule.sol b/solidity/contracts/modules/response/BondedResponseModule.sol index 79f3d565..ba41b872 100644 --- a/solidity/contracts/modules/response/BondedResponseModule.sol +++ b/solidity/contracts/modules/response/BondedResponseModule.sol @@ -28,7 +28,8 @@ contract BondedResponseModule is Module, IBondedResponseModule { RequestParameters memory _params = decodeRequestData(_request.responseModuleData); // Cannot propose after the deadline - if (block.timestamp >= _params.deadline) revert BondedResponseModule_TooLateToPropose(); + uint256 _requestCreatedAt = ORACLE.requestCreatedAt(_response.requestId); + if (block.timestamp >= _requestCreatedAt + _params.deadline) revert BondedResponseModule_TooLateToPropose(); // Cannot propose to a request with a response, unless the response is being disputed bytes32[] memory _responseIds = ORACLE.getResponseIds(_response.requestId); @@ -75,12 +76,11 @@ contract BondedResponseModule is Module, IBondedResponseModule { bool _isModule = ORACLE.allowedModule(_response.requestId, _finalizer); - if (!_isModule && block.timestamp < _params.deadline) { + if (!_isModule && block.timestamp < ORACLE.requestCreatedAt(_response.requestId) + _params.deadline) { revert BondedResponseModule_TooEarlyToFinalize(); } uint256 _responseCreatedAt = ORACLE.responseCreatedAt(_getId(_response)); - if (_responseCreatedAt != 0) { if (!_isModule && block.timestamp < _responseCreatedAt + _params.disputeWindow) { revert BondedResponseModule_TooEarlyToFinalize(); diff --git a/solidity/interfaces/modules/dispute/IBondEscalationModule.sol b/solidity/interfaces/modules/dispute/IBondEscalationModule.sol index 8059f5aa..4ee78546 100644 --- a/solidity/interfaces/modules/dispute/IBondEscalationModule.sol +++ b/solidity/interfaces/modules/dispute/IBondEscalationModule.sol @@ -134,7 +134,8 @@ interface IBondEscalationModule is IDisputeModule { * @param bondToken Address of the token associated with the given request * @param bondSize Amount to bond to dispute or propose an answer for the given request * @param maxNumberOfEscalations Maximum allowed escalations or pledges for each side during the bond escalation process - * @param bondEscalationDeadline Timestamp at which bond escalation process finishes when pledges are not tied + * @param bondEscalationDeadline Number of seconds after dispute creation required to + * finish the bond escalation process when pledges are not tied. * @param tyingBuffer Number of seconds to extend the bond escalation process to allow the losing * party to tie if at the end of the initial deadline the pledges weren't tied. * @param disputeWindow Number of seconds disputers have to challenge the proposed response since its creation. diff --git a/solidity/test/integration/BondEscalation.t.sol b/solidity/test/integration/BondEscalation.t.sol index b1704a96..f68c5d55 100644 --- a/solidity/test/integration/BondEscalation.t.sol +++ b/solidity/test/integration/BondEscalation.t.sol @@ -23,8 +23,8 @@ contract Integration_BondEscalation is IntegrationBase { function setUp() public override { super.setUp(); - _expectedDeadline = block.timestamp + 10 days; - _bondEscalationDeadline = block.timestamp + 5 days; + _expectedDeadline = 10 days; + _bondEscalationDeadline = 5 days; setUpRequest(); setUpEscalation(); @@ -128,7 +128,7 @@ contract Integration_BondEscalation is IntegrationBase { // Step 4: Disputer runs out of capital // Step 5: External parties see that Disputer's dispute was wrong so they don't join to escalate // Step 6: Proposer response's is deemed correct and final once the bond escalation window is over - vm.warp(_expectedDeadline + _tyingBuffer + 1); + vm.warp(block.timestamp + _expectedDeadline + _tyingBuffer + 1); _bondEscalationModule.settleBondEscalation(mockRequest, mockResponse, mockDispute); IOracle.DisputeStatus _disputeStatus = oracle.disputeStatus(_disputeId); @@ -152,7 +152,7 @@ contract Integration_BondEscalation is IntegrationBase { assertEq(_bondEscalationAccounting.balanceOf(disputer, usdc), 0, 'Mismatch: Disputer balance'); // Step 8: Finalize request and check balances again - vm.warp(_expectedDeadline + 1 days); + vm.warp(block.timestamp + _expectedDeadline + 1 days); oracle.finalize(mockRequest, mockResponse); // Test: The requester has no balance because he has paid the proposer @@ -189,7 +189,7 @@ contract Integration_BondEscalation is IntegrationBase { // External parties see that Proposer's proposal was wrong so they don't join to escalate // Step 5: Proposer response's is deemed incorrect. The bond escalation process along with the tying buffer is terminated - vm.warp(_bondEscalationDeadline + _tyingBuffer + 1); + vm.warp(block.timestamp + _bondEscalationDeadline + _tyingBuffer + 1); _bondEscalationModule.settleBondEscalation(mockRequest, mockResponse, mockDispute); IOracle.DisputeStatus _disputeStatus = oracle.disputeStatus(_disputeId); @@ -248,7 +248,7 @@ contract Integration_BondEscalation is IntegrationBase { _responseId = oracle.proposeResponse(mockRequest, _thirdResponse); // Step 11: It goes undisputed for three days, therefore it's deemed correct and final - vm.warp(_expectedDeadline + 1); + vm.warp(block.timestamp + _expectedDeadline + 1); oracle.finalize(mockRequest, _thirdResponse); // Test: The requester has paid out the reward @@ -283,7 +283,7 @@ contract Integration_BondEscalation is IntegrationBase { // Step 12: Two days after the deadline, the resolution module says that Another proposer's answer was correct // So Another proposer gets paid Disputer's bond - vm.warp(_expectedDeadline + 2 days); + vm.warp(block.timestamp + _expectedDeadline + 2 days); _mockArbitrator.setAnswer(IOracle.DisputeStatus.Lost); oracle.resolveDispute(mockRequest, _secondResponse, _secondDispute); @@ -340,7 +340,7 @@ contract Integration_BondEscalation is IntegrationBase { // Step 4: Disputer runs out of capital // Step 5: The tying buffer kicks in - vm.warp(_bondEscalationDeadline + 1); + vm.warp(block.timestamp + _bondEscalationDeadline + 1); // Step 6: An external party sees that Proposer's response is incorrect, so they bond the required WETH _deposit(_bondEscalationAccounting, _secondDisputer, usdc, _pledgeSize); @@ -420,7 +420,7 @@ contract Integration_BondEscalation is IntegrationBase { _bondEscalationModule.pledgeForDispute(mockRequest, mockDispute); // Step 6: Proposer loses in resolution - vm.warp(_bondEscalationDeadline + 1); + vm.warp(block.timestamp + _bondEscalationDeadline + 1); oracle.escalateDispute(mockRequest, mockResponse, mockDispute); oracle.resolveDispute(mockRequest, mockResponse, mockDispute); @@ -470,7 +470,7 @@ contract Integration_BondEscalation is IntegrationBase { // Step 4: Disputer runs out of capital // Step 5: The tying buffer kicks in - vm.warp(_bondEscalationDeadline + 1); + vm.warp(block.timestamp + _bondEscalationDeadline + 1); // Step 6: An external party sees that Proposer's response is incorrect, so they bond the required WETH _deposit(_bondEscalationAccounting, _secondDisputer, usdc, _pledgeSize); @@ -531,7 +531,7 @@ contract Integration_BondEscalation is IntegrationBase { // Step 4: Disputer runs out of capital // Step 5: The tying buffer kicks in - vm.warp(_bondEscalationDeadline + 1); + vm.warp(block.timestamp + _bondEscalationDeadline + 1); // Step 6: An external party sees that Proposer's response is incorrect, so they bond the required WETH _deposit(_bondEscalationAccounting, _secondDisputer, usdc, _pledgeSize); diff --git a/solidity/test/integration/EscalateDispute.t.sol b/solidity/test/integration/EscalateDispute.t.sol index 2ce44afd..93471b37 100644 --- a/solidity/test/integration/EscalateDispute.t.sol +++ b/solidity/test/integration/EscalateDispute.t.sol @@ -435,7 +435,7 @@ contract Integration_EscalateDispute is IntegrationBase { ); // We escalate the dispute - _mineBlocks(_blocksDeadline + 1); + vm.warp(block.timestamp + _expectedDeadline + 1); oracle.escalateDispute(mockRequest, mockResponse, mockDispute); // We check that the dispute was escalated diff --git a/solidity/test/integration/Finalization.t.sol b/solidity/test/integration/Finalization.t.sol index 99df4b16..be4c2d0a 100644 --- a/solidity/test/integration/Finalization.t.sol +++ b/solidity/test/integration/Finalization.t.sol @@ -27,7 +27,7 @@ contract Integration_Finalization is IntegrationBase { _proposeResponse(); // Traveling to the end of the dispute window - vm.warp(_expectedDeadline + 1 + _baseDisputeWindow); + vm.warp(block.timestamp + _expectedDeadline + 1 + _baseDisputeWindow); // Check: all external calls are made? vm.expectCall(address(_mockCallback), abi.encodeWithSelector(IProphetCallback.prophetCallback.selector, _calldata)); @@ -70,7 +70,7 @@ contract Integration_Finalization is IntegrationBase { * @notice Finalizing a request with a ongoing dispute reverts. */ function test_revertFinalizeInDisputeWindow(uint256 _timestamp) public { - _timestamp = bound(_timestamp, block.timestamp, _expectedDeadline - _baseDisputeWindow - 1); + _timestamp = bound(_timestamp, block.timestamp, block.timestamp + _expectedDeadline - _baseDisputeWindow - 1); _createRequest(); _proposeResponse(); @@ -96,7 +96,7 @@ contract Integration_Finalization is IntegrationBase { _proposeResponse(); // Traveling to the end of the dispute window - vm.warp(_expectedDeadline + 1 + _baseDisputeWindow); + vm.warp(block.timestamp + _expectedDeadline + 1 + _baseDisputeWindow); vm.expectCall(address(_mockCallback), abi.encodeWithSelector(IProphetCallback.prophetCallback.selector, _calldata)); vm.prank(_finalizer); diff --git a/solidity/test/integration/IntegrationBase.sol b/solidity/test/integration/IntegrationBase.sol index c918832e..d9fe9d64 100644 --- a/solidity/test/integration/IntegrationBase.sol +++ b/solidity/test/integration/IntegrationBase.sol @@ -79,12 +79,11 @@ contract IntegrationBase is DSTestPlus, TestConstants, Helpers { string internal _expectedResponse = '{"ethereum":{"usd":1000}}'; uint256 internal _expectedBondSize = 100 ether; uint256 internal _expectedReward = 30 ether; - uint256 internal _expectedDeadline; uint256 internal _expectedCallbackValue = 42; - uint256 internal _baseDisputeWindow = 120 * BLOCK_TIME; bytes32 internal _ipfsHash = bytes32('QmR4uiJH654k3Ta2uLLQ8r'); uint256 internal _blocksDeadline = 600; - uint256 internal _timestampDeadline = _blocksDeadline * BLOCK_TIME; + uint256 internal _baseDisputeWindow = 6 hours; + uint256 internal _expectedDeadline = 10 days; function setUp() public virtual { vm.createSelectFork(vm.rpcUrl('optimism'), FORK_BLOCK); @@ -135,9 +134,6 @@ contract IntegrationBase is DSTestPlus, TestConstants, Helpers { _mockArbitrator = new MockArbitrator(); vm.stopPrank(); - // Set the expected deadline - _expectedDeadline = block.timestamp + _timestampDeadline; - // Configure the mock request mockRequest.requestModuleData = abi.encode( IHttpRequestModule.RequestParameters({ diff --git a/solidity/test/integration/Payments.t.sol b/solidity/test/integration/Payments.t.sol index 7b4edab0..ecee5489 100644 --- a/solidity/test/integration/Payments.t.sol +++ b/solidity/test/integration/Payments.t.sol @@ -26,7 +26,7 @@ contract Integration_Payments is IntegrationBase { assertEq(_accountingExtension.bondedAmountOf(proposer, usdc, _requestId), _bondSize); // Warp to finalization time. - vm.warp(_expectedDeadline + _baseDisputeWindow); + vm.warp(block.timestamp + _expectedDeadline + _baseDisputeWindow); // Finalize request/response oracle.finalize(mockRequest, mockResponse); @@ -66,7 +66,7 @@ contract Integration_Payments is IntegrationBase { assertEq(_accountingExtension.bondedAmountOf(proposer, weth, _requestId), _bondSize); // Warp to finalization time. - vm.warp(_expectedDeadline + _baseDisputeWindow); + vm.warp(block.timestamp + _expectedDeadline + _baseDisputeWindow); // Finalize request/response. oracle.finalize(mockRequest, mockResponse); diff --git a/solidity/test/integration/ResponseDispute.t.sol b/solidity/test/integration/ResponseDispute.t.sol index 566cd303..bcea40bd 100644 --- a/solidity/test/integration/ResponseDispute.t.sol +++ b/solidity/test/integration/ResponseDispute.t.sol @@ -83,7 +83,7 @@ contract Integration_ResponseDispute is IntegrationBase { * @notice Disputing a finalized response should revert */ function test_disputeResponse_alreadyFinalized() public { - vm.warp(_expectedDeadline + _baseDisputeWindow); + vm.warp(block.timestamp + _expectedDeadline + _baseDisputeWindow); oracle.finalize(mockRequest, mockResponse); vm.expectRevert(abi.encodeWithSelector(IOracle.Oracle_AlreadyFinalized.selector, _getId(mockRequest))); diff --git a/solidity/test/integration/ResponseProposal.t.sol b/solidity/test/integration/ResponseProposal.t.sol index 8a8ee1af..ea0d0e10 100644 --- a/solidity/test/integration/ResponseProposal.t.sol +++ b/solidity/test/integration/ResponseProposal.t.sol @@ -47,11 +47,11 @@ contract Integration_ResponseProposal is IntegrationBase { /** * @notice Proposing a response after the deadline reverts */ - function test_proposeResponse_afterDeadline(uint256 _timestamp, bytes memory _responseBytes) public { - vm.assume(_timestamp > _expectedDeadline); + function test_proposeResponse_afterDeadline(uint256 _secondsAfter, bytes memory _responseBytes) public { + _secondsAfter = bound(_secondsAfter, 1, type(uint248).max); // Warp to timestamp after deadline - vm.warp(_timestamp); + vm.warp(block.timestamp + _expectedDeadline + _secondsAfter); mockResponse.response = _responseBytes; diff --git a/solidity/test/integration/RootVerification.t.sol b/solidity/test/integration/RootVerification.t.sol index 92dadd1f..52792fde 100644 --- a/solidity/test/integration/RootVerification.t.sol +++ b/solidity/test/integration/RootVerification.t.sol @@ -106,7 +106,7 @@ contract Integration_RootVerification is IntegrationBase { vm.prank(proposer); oracle.proposeResponse(mockRequest, mockResponse); - vm.warp(_expectedDeadline + _baseDisputeWindow); + vm.warp(block.timestamp + _expectedDeadline + _baseDisputeWindow); oracle.finalize(mockRequest, mockResponse); } diff --git a/solidity/test/unit/modules/dispute/BondEscalationAccounting.t.sol b/solidity/test/unit/modules/dispute/BondEscalationAccounting.t.sol index fc2e74ca..c6761eea 100644 --- a/solidity/test/unit/modules/dispute/BondEscalationAccounting.t.sol +++ b/solidity/test/unit/modules/dispute/BondEscalationAccounting.t.sol @@ -71,6 +71,11 @@ contract BaseTest is Test, Helpers { address public authorizedCaller = makeAddr('authorizedCaller'); address public unauthorizedCaller = makeAddr('unauthorizedCaller'); + // Mock timestamps + uint256 public requestCreatedAt; + uint256 public responseCreatedAt; + uint256 public disputeCreatedAt; + // Pledged Event event Pledged( address indexed _pledger, bytes32 indexed _requestId, bytes32 indexed _disputeId, IERC20 _token, uint256 _amount @@ -111,6 +116,12 @@ contract BaseTest is Test, Helpers { token = IERC20(makeAddr('ERC20')); vm.etch(address(token), hex'069420'); + // Avoid starting at 0 for time sensitive tests + vm.warp(123_456_789); + requestCreatedAt = block.timestamp; + responseCreatedAt = requestCreatedAt + 30 seconds; + disputeCreatedAt = requestCreatedAt + 1 minutes; + address[] memory _authorizedCallers = new address[](1); _authorizedCallers[0] = authorizedCaller; bondEscalationAccounting = new ForTest_BondEscalationAccounting(oracle, _authorizedCallers); @@ -135,7 +146,7 @@ contract BondEscalationAccounting_Unit_Pledge is BaseTest { } function test_revertIfDisallowedModule(address _pledger, uint256 _amount) public { - (, IOracle.Dispute memory _dispute) = _getResponseAndDispute(oracle); + (, IOracle.Dispute memory _dispute) = _getResponseAndDispute(oracle, disputeCreatedAt); // Mock and expect the call to oracle checking if the module is allowed _mockAndExpect( @@ -179,7 +190,7 @@ contract BondEscalationAccounting_Unit_Pledge is BaseTest { } function test_successfulCall(address _pledger, uint256 _amount) public { - (, IOracle.Dispute memory _dispute) = _getResponseAndDispute(oracle); + (, IOracle.Dispute memory _dispute) = _getResponseAndDispute(oracle, disputeCreatedAt); bytes32 _requestId = _getId(mockRequest); bytes32 _disputeId = _getId(_dispute); @@ -234,7 +245,7 @@ contract BondEscalationAccounting_Unit_OnSettleBondEscalation is BaseTest { } function test_revertIfDisallowedModule(uint256 _numOfWinningPledgers, uint256 _amountPerPledger) public { - (, IOracle.Dispute memory _dispute) = _getResponseAndDispute(oracle); + (, IOracle.Dispute memory _dispute) = _getResponseAndDispute(oracle, disputeCreatedAt); bytes32 _requestId = _getId(mockRequest); // Mock and expect the call to oracle checking if the module is allowed @@ -325,7 +336,7 @@ contract BondEscalationAccounting_Unit_OnSettleBondEscalation is BaseTest { } function test_successfulCall(uint256 _numOfWinningPledgers, uint256 _amountPerPledger) public { - (, IOracle.Dispute memory _dispute) = _getResponseAndDispute(oracle); + (, IOracle.Dispute memory _dispute) = _getResponseAndDispute(oracle, disputeCreatedAt); bytes32 _requestId = _getId(mockRequest); bytes32 _disputeId = _getId(_dispute); @@ -333,7 +344,7 @@ contract BondEscalationAccounting_Unit_OnSettleBondEscalation is BaseTest { _numOfWinningPledgers = bound(_numOfWinningPledgers, 1, 30); _amountPerPledger = bound(_amountPerPledger, 1, type(uint256).max / _numOfWinningPledgers); - // Mock and expect the call to oracle checking if the module is allowed + // // Mock and expect the call to oracle checking if the module is allowed _mockAndExpect( address(oracle), abi.encodeCall(IOracle.allowedModule, (_requestId, authorizedCaller)), abi.encode(true) ); @@ -389,7 +400,7 @@ contract BondEscalationAccounting_Unit_ReleasePledge is BaseTest { } function test_revertIfDisallowedModule(address _pledger, uint256 _amount) public { - (, IOracle.Dispute memory _dispute) = _getResponseAndDispute(oracle); + (, IOracle.Dispute memory _dispute) = _getResponseAndDispute(oracle, disputeCreatedAt); bytes32 _requestId = _getId(mockRequest); // Mock and expect the call to oracle checking if the module is allowed @@ -439,7 +450,7 @@ contract BondEscalationAccounting_Unit_ReleasePledge is BaseTest { } function test_successfulCall(uint256 _amount, address _pledger) public { - (, IOracle.Dispute memory _dispute) = _getResponseAndDispute(oracle); + (, IOracle.Dispute memory _dispute) = _getResponseAndDispute(oracle, disputeCreatedAt); bytes32 _requestId = _getId(mockRequest); bytes32 _disputeId = _getId(_dispute); diff --git a/solidity/test/unit/modules/dispute/BondEscalationModule.t.sol b/solidity/test/unit/modules/dispute/BondEscalationModule.t.sol index 31f18815..219cb8f6 100644 --- a/solidity/test/unit/modules/dispute/BondEscalationModule.t.sol +++ b/solidity/test/unit/modules/dispute/BondEscalationModule.t.sol @@ -75,6 +75,10 @@ contract BaseTest is Test, Helpers { uint256 public tyingBuffer; // Mock dispute window uint256 public disputeWindow; + // Mock creation timestamps + uint256 public requestCreatedAt; + uint256 public responseCreatedAt; + uint256 public disputeCreatedAt; // Events event PledgedForDispute(bytes32 indexed _disputeId, address indexed _pledger, uint256 indexed _amount); @@ -104,7 +108,12 @@ contract BaseTest is Test, Helpers { disputeWindow = type(uint128).max; // Avoid starting at 0 for time sensitive tests - vm.warp(123_456); + vm.warp(123_456_789); + requestCreatedAt = block.timestamp; + responseCreatedAt = requestCreatedAt + 30 seconds; + disputeCreatedAt = requestCreatedAt + 1 minutes; + + bondEscalationDeadline = 3 days; mockDispute = IOracle.Dispute({ disputer: disputer, @@ -208,7 +217,7 @@ contract BondEscalationModule_Unit_EscalateDispute is BaseTest { assumeFuzzable(address(_params.accountingExtension)) { // Set _bondEscalationDeadline to be the current timestamp to reach the second condition. - _params.bondEscalationDeadline = block.timestamp; + _params.bondEscalationDeadline = 3 days; mockRequest.disputeModuleData = abi.encode(_params); bytes32 _requestId = _getId(mockRequest); @@ -220,6 +229,10 @@ contract BondEscalationModule_Unit_EscalateDispute is BaseTest { address(oracle), abi.encodeCall(IOracle.disputeStatus, (_disputeId)), abi.encode(IOracle.DisputeStatus.Escalated) ); + _mockAndExpect( + address(oracle), abi.encodeCall(IOracle.disputeCreatedAt, (_disputeId)), abi.encode(disputeCreatedAt) + ); + // Setting this dispute as the one going through the bond escalation process, as the user can only // dispute once before the bond escalation deadline is over, and that dispute goes through the escalation module. bondEscalationModule.forTest_setEscalatedDispute(_requestId, _disputeId); @@ -250,7 +263,7 @@ contract BondEscalationModule_Unit_EscalateDispute is BaseTest { // Set a tying buffer to show that this can happen even in the tying buffer if the dispute was settled _params.tyingBuffer = 1000; // Make the current timestamp be greater than the bond escalation deadline - _params.bondEscalationDeadline = block.timestamp - 1; + _params.bondEscalationDeadline = bondEscalationDeadline; mockRequest.disputeModuleData = abi.encode(_params); bytes32 _requestId = _getId(mockRequest); @@ -263,6 +276,10 @@ contract BondEscalationModule_Unit_EscalateDispute is BaseTest { _requestId, IBondEscalationModule.BondEscalationStatus(_status) ); + _mockAndExpect( + address(oracle), abi.encodeCall(IOracle.disputeCreatedAt, (_disputeId)), abi.encode(disputeCreatedAt) + ); + _mockAndExpect( address(oracle), abi.encodeCall(IOracle.disputeStatus, (_disputeId)), abi.encode(IOracle.DisputeStatus.Escalated) ); @@ -270,6 +287,8 @@ contract BondEscalationModule_Unit_EscalateDispute is BaseTest { // Set the dispute to be the one that went through the bond escalation process for the given requestId bondEscalationModule.forTest_setEscalatedDispute(_requestId, _disputeId); + vm.warp(disputeCreatedAt + _params.bondEscalationDeadline + 1); + // Check: does it revert if the dispute is not escalatable? vm.expectRevert(IBondEscalationModule.BondEscalationModule_NotEscalatable.selector); vm.prank(address(oracle)); @@ -292,7 +311,7 @@ contract BondEscalationModule_Unit_EscalateDispute is BaseTest { // Set a tying buffer to make the test more explicit _params.tyingBuffer = 1000; // Set bond escalation deadline to be the current timestamp. We will warp this. - _params.bondEscalationDeadline = block.timestamp; + _params.bondEscalationDeadline = bondEscalationDeadline; mockRequest.disputeModuleData = abi.encode(_params); bytes32 _requestId = _getId(mockRequest); @@ -305,7 +324,7 @@ contract BondEscalationModule_Unit_EscalateDispute is BaseTest { uint256 _numAgainstPledgers = 2; // Warp the current timestamp so we are past the tyingBuffer - vm.warp(_params.bondEscalationDeadline + _params.tyingBuffer + 1); + vm.warp(disputeCreatedAt + _params.bondEscalationDeadline + _params.tyingBuffer + 1); // Set the bond escalation status of the given requestId to Active bondEscalationModule.forTest_setBondEscalationStatus(_requestId, IBondEscalationModule.BondEscalationStatus.Active); @@ -316,6 +335,10 @@ contract BondEscalationModule_Unit_EscalateDispute is BaseTest { // Set the number of pledgers for both sides _setBondEscalation(_requestId, _numForPledgers, _numAgainstPledgers); + _mockAndExpect( + address(oracle), abi.encodeCall(IOracle.disputeCreatedAt, (_disputeId)), abi.encode(disputeCreatedAt) + ); + _mockAndExpect( address(oracle), abi.encodeCall(IOracle.disputeStatus, (_disputeId)), abi.encode(IOracle.DisputeStatus.Escalated) ); @@ -341,7 +364,7 @@ contract BondEscalationModule_Unit_EscalateDispute is BaseTest { IBondEscalationModule.RequestParameters memory _params ) public assumeFuzzable(address(_params.accountingExtension)) { // Set bond escalation deadline to be the current timestamp. We will warp this. - _params.bondEscalationDeadline = block.timestamp; + _params.bondEscalationDeadline = bondEscalationDeadline; // Set a tying buffer _params.tyingBuffer = 1000; _params.accountingExtension = IBondEscalationAccounting(makeAddr('BondEscalationAccounting')); @@ -364,7 +387,7 @@ contract BondEscalationModule_Unit_EscalateDispute is BaseTest { uint256 _numAgainstPledgers = 2; // Warp so we are still in the tying buffer period. This is to show a dispute can be escalated during the buffer if the pledges are tied. - vm.warp(_params.bondEscalationDeadline + _params.tyingBuffer); + vm.warp(disputeCreatedAt + _params.bondEscalationDeadline + _params.tyingBuffer); // Set the bond escalation status of the given requestId to Active bondEscalationModule.forTest_setBondEscalationStatus(_requestId, IBondEscalationModule.BondEscalationStatus.Active); @@ -379,6 +402,10 @@ contract BondEscalationModule_Unit_EscalateDispute is BaseTest { vm.expectEmit(true, true, true, true, address(bondEscalationModule)); emit BondEscalationStatusUpdated(_requestId, _disputeId, IBondEscalationModule.BondEscalationStatus.Escalated); + _mockAndExpect( + address(oracle), abi.encodeCall(IOracle.disputeCreatedAt, (_disputeId)), abi.encode(disputeCreatedAt) + ); + // Mock and expect IOracle.disputeStatus to be called _mockAndExpect( address(oracle), abi.encodeCall(IOracle.disputeStatus, (_disputeId)), abi.encode(IOracle.DisputeStatus.Escalated) @@ -444,10 +471,14 @@ contract BondEscalationModule_Unit_DisputeResponse is BaseTest { uint256 _timestamp, IBondEscalationModule.RequestParameters memory _params ) public assumeFuzzable(address(_params.accountingExtension)) { - _timestamp = bound(_timestamp, 1, 365 days); + _timestamp = bound(_timestamp, block.timestamp, type(uint128).max); + + // update mock timestamp + responseCreatedAt = _timestamp; + // Set deadline to timestamp so we are still in the bond escalation period - _params.bondEscalationDeadline = _timestamp - 1; - _params.disputeWindow = _timestamp + 1; + _params.bondEscalationDeadline = 3 days; + _params.disputeWindow = 5 days; mockRequest.disputeModuleData = abi.encode(_params); bytes32 _requestId = _getId(mockRequest); @@ -458,7 +489,12 @@ contract BondEscalationModule_Unit_DisputeResponse is BaseTest { mockDispute.requestId = _requestId; // Mock and expect IOracle.responseCreatedAt to be called - _mockAndExpect(address(oracle), abi.encodeCall(IOracle.responseCreatedAt, (_responseId)), abi.encode(1)); + _mockAndExpect( + address(oracle), abi.encodeCall(IOracle.responseCreatedAt, (_responseId)), abi.encode(responseCreatedAt) + ); + _mockAndExpect( + address(oracle), abi.encodeCall(IOracle.disputeCreatedAt, (_getId(mockDispute))), abi.encode(_timestamp) + ); // Mock and expect the accounting extension to be called _mockAndExpect( @@ -469,7 +505,7 @@ contract BondEscalationModule_Unit_DisputeResponse is BaseTest { abi.encode(true) ); - vm.warp(_timestamp); + vm.warp(_timestamp + _params.bondEscalationDeadline + 1); // Check: does it revert if the bond escalation is over? vm.expectRevert(IBondEscalationModule.BondEscalationModule_BondEscalationOver.selector); @@ -486,8 +522,8 @@ contract BondEscalationModule_Unit_DisputeResponse is BaseTest { IBondEscalationModule.RequestParameters memory _params ) public assumeFuzzable(address(_params.accountingExtension)) { // Set deadline to timestamp so we are still in the bond escalation period - _params.disputeWindow = block.timestamp; - _params.bondEscalationDeadline = block.timestamp; + _params.disputeWindow = 2 days; + _params.bondEscalationDeadline = bondEscalationDeadline; // Compute proper IDs mockRequest.disputeModuleData = abi.encode(_params); @@ -511,7 +547,13 @@ contract BondEscalationModule_Unit_DisputeResponse is BaseTest { ); // Mock and expect IOracle.responseCreatedAt to be called - _mockAndExpect(address(oracle), abi.encodeCall(IOracle.responseCreatedAt, (_responseId)), abi.encode(1)); + _mockAndExpect( + address(oracle), abi.encodeCall(IOracle.responseCreatedAt, (_responseId)), abi.encode(responseCreatedAt) + ); + + _mockAndExpect( + address(oracle), abi.encodeCall(IOracle.disputeCreatedAt, (_disputeId)), abi.encode(disputeCreatedAt) + ); vm.expectEmit(true, true, true, true, address(bondEscalationModule)); emit ResponseDisputed({ @@ -543,8 +585,8 @@ contract BondEscalationModule_Unit_DisputeResponse is BaseTest { address _proposer, IBondEscalationModule.RequestParameters memory _params ) public assumeFuzzable(address(_params.accountingExtension)) { - _params.disputeWindow = block.timestamp; - _params.bondEscalationDeadline = block.timestamp; + _params.disputeWindow = 2 days; + _params.bondEscalationDeadline = bondEscalationDeadline; // Compute proper IDs mockRequest.disputeModuleData = abi.encode(_params); @@ -559,8 +601,14 @@ contract BondEscalationModule_Unit_DisputeResponse is BaseTest { mockDispute.responseId = _responseId; bytes32 _disputeId = _getId(mockDispute); + _mockAndExpect( + address(oracle), abi.encodeCall(IOracle.responseCreatedAt, (_responseId)), abi.encode(responseCreatedAt) + ); + // Mock and expect IOracle.responseCreatedAt to be called - _mockAndExpect(address(oracle), abi.encodeCall(IOracle.responseCreatedAt, (_responseId)), abi.encode(1)); + _mockAndExpect( + address(oracle), abi.encodeCall(IOracle.disputeCreatedAt, (_disputeId)), abi.encode(disputeCreatedAt) + ); // Mock and expect the accounting extension to be called _mockAndExpect( @@ -955,7 +1003,7 @@ contract BondEscalationModule_Unit_PledgeForDispute is BaseTest { { _params.bondSize = 1; _params.maxNumberOfEscalations = 1; - _params.bondEscalationDeadline = block.timestamp; + _params.bondEscalationDeadline = bondEscalationDeadline; _params.tyingBuffer = 1000; mockRequest.disputeModuleData = abi.encode(_params); @@ -965,7 +1013,7 @@ contract BondEscalationModule_Unit_PledgeForDispute is BaseTest { bondEscalationModule.forTest_setEscalatedDispute(_requestId, _disputeId); - vm.warp(_params.bondEscalationDeadline + _params.tyingBuffer + 1); + vm.warp(disputeCreatedAt + _params.bondEscalationDeadline + _params.tyingBuffer + 1); // Check: does it revert if the bond escalation is over? vm.expectRevert(IBondEscalationModule.BondEscalationModule_BondEscalationOver.selector); @@ -981,7 +1029,7 @@ contract BondEscalationModule_Unit_PledgeForDispute is BaseTest { { _params.bondSize = 1; _params.maxNumberOfEscalations = 2; - _params.bondEscalationDeadline = block.timestamp - 1; + _params.bondEscalationDeadline = bondEscalationDeadline; _params.tyingBuffer = 1000; mockRequest.disputeModuleData = abi.encode(_params); @@ -1011,7 +1059,7 @@ contract BondEscalationModule_Unit_PledgeForDispute is BaseTest { _params.tyingBuffer = bound(_params.tyingBuffer, 0, type(uint128).max); _params.bondSize = 1; _params.maxNumberOfEscalations = 3; - _params.bondEscalationDeadline = block.timestamp + 1; + _params.bondEscalationDeadline = bondEscalationDeadline; mockRequest.disputeModuleData = abi.encode(_params); (, IOracle.Dispute memory _dispute) = _getResponseAndDispute(oracle); @@ -1039,7 +1087,7 @@ contract BondEscalationModule_Unit_PledgeForDispute is BaseTest { { _params.bondSize = 1; _params.maxNumberOfEscalations = 3; - _params.bondEscalationDeadline = block.timestamp - 1; + _params.bondEscalationDeadline = bondEscalationDeadline; _params.tyingBuffer = 1000; mockRequest.disputeModuleData = abi.encode(_params); @@ -1054,6 +1102,9 @@ contract BondEscalationModule_Unit_PledgeForDispute is BaseTest { _setBondEscalation(_requestId, _numForPledgers, _numAgainstPledgers); + // warp into the tyingBuffer + vm.warp(disputeCreatedAt + _params.bondEscalationDeadline + 1); + // Check: does it revert if trying to tie outside of the tying buffer? vm.expectRevert(IBondEscalationModule.BondEscalationModule_CannotBreakTieDuringTyingBuffer.selector); bondEscalationModule.pledgeForDispute(mockRequest, _dispute); @@ -1068,7 +1119,7 @@ contract BondEscalationModule_Unit_PledgeForDispute is BaseTest { { _params.bondSize = 1000; _params.maxNumberOfEscalations = 3; - _params.bondEscalationDeadline = block.timestamp - 1; + _params.bondEscalationDeadline = bondEscalationDeadline; _params.tyingBuffer = 1000; mockRequest.disputeModuleData = abi.encode(_params); @@ -1139,7 +1190,7 @@ contract BondEscalationModule_Unit_PledgeAgainstDispute is BaseTest { { _params.bondSize = 1; _params.maxNumberOfEscalations = 1; - _params.bondEscalationDeadline = block.timestamp; + _params.bondEscalationDeadline = 1 days; _params.tyingBuffer = 1000; mockRequest.disputeModuleData = abi.encode(_params); @@ -1149,11 +1200,10 @@ contract BondEscalationModule_Unit_PledgeAgainstDispute is BaseTest { bondEscalationModule.forTest_setEscalatedDispute(_requestId, _disputeId); - vm.warp(_params.bondEscalationDeadline + _params.tyingBuffer + 1); + vm.warp(disputeCreatedAt + _params.bondEscalationDeadline + _params.tyingBuffer + 1); // Check: does it revert if the bond escalation is over? vm.expectRevert(IBondEscalationModule.BondEscalationModule_BondEscalationOver.selector); - bondEscalationModule.pledgeAgainstDispute(mockRequest, _dispute); } @@ -1166,7 +1216,7 @@ contract BondEscalationModule_Unit_PledgeAgainstDispute is BaseTest { { _params.bondSize = 1; _params.maxNumberOfEscalations = 2; - _params.bondEscalationDeadline = block.timestamp - 1; + _params.bondEscalationDeadline = bondEscalationDeadline; _params.tyingBuffer = 1000; mockRequest.disputeModuleData = abi.encode(_params); @@ -1198,7 +1248,7 @@ contract BondEscalationModule_Unit_PledgeAgainstDispute is BaseTest { _params.tyingBuffer = bound(_params.tyingBuffer, 0, type(uint128).max); _params.bondSize = 1; _params.maxNumberOfEscalations = 3; - _params.bondEscalationDeadline = block.timestamp + 1; + _params.bondEscalationDeadline = bondEscalationDeadline; mockRequest.disputeModuleData = abi.encode(_params); (, IOracle.Dispute memory _dispute) = _getResponseAndDispute(oracle); @@ -1229,7 +1279,7 @@ contract BondEscalationModule_Unit_PledgeAgainstDispute is BaseTest { // Set mock request parameters _params.bondSize = 1; _params.maxNumberOfEscalations = 3; - _params.bondEscalationDeadline = block.timestamp - 1; + _params.bondEscalationDeadline = 3 days; _params.tyingBuffer = 1000; mockRequest.disputeModuleData = abi.encode(_params); @@ -1244,6 +1294,8 @@ contract BondEscalationModule_Unit_PledgeAgainstDispute is BaseTest { _setBondEscalation(_requestId, _numForPledgers, _numAgainstPledgers); + vm.warp(disputeCreatedAt + _params.bondEscalationDeadline + 1); + // Check: does it revert if trying to tie outside of the tying buffer? vm.expectRevert(IBondEscalationModule.BondEscalationModule_CannotBreakTieDuringTyingBuffer.selector); bondEscalationModule.pledgeAgainstDispute(mockRequest, _dispute); @@ -1258,7 +1310,7 @@ contract BondEscalationModule_Unit_PledgeAgainstDispute is BaseTest { { _params.bondSize = 1000; _params.maxNumberOfEscalations = 3; - _params.bondEscalationDeadline = block.timestamp - 1; + _params.bondEscalationDeadline = bondEscalationDeadline; _params.tyingBuffer = 1000; mockRequest.disputeModuleData = abi.encode(_params); @@ -1328,11 +1380,15 @@ contract BondEscalationModule_Unit_SettleBondEscalation is BaseTest { assumeFuzzable(address(_params.accountingExtension)) { _params.tyingBuffer = bound(_params.tyingBuffer, 0, type(uint128).max); - _params.bondEscalationDeadline = block.timestamp; + _params.bondEscalationDeadline = bondEscalationDeadline; mockRequest.disputeModuleData = abi.encode(_params); (IOracle.Response memory _response, IOracle.Dispute memory _dispute) = _getResponseAndDispute(oracle); + _mockAndExpect( + address(oracle), abi.encodeCall(IOracle.disputeCreatedAt, (_getId(_dispute))), abi.encode(disputeCreatedAt) + ); + // Check: does it revert if the bond escalation is not over? vm.expectRevert(IBondEscalationModule.BondEscalationModule_BondEscalationNotOver.selector); bondEscalationModule.settleBondEscalation(mockRequest, _response, _dispute); @@ -1346,14 +1402,18 @@ contract BondEscalationModule_Unit_SettleBondEscalation is BaseTest { public assumeFuzzable(address(_params.accountingExtension)) { - _params.bondEscalationDeadline = block.timestamp; + _params.bondEscalationDeadline = bondEscalationDeadline; _params.tyingBuffer = 1000; mockRequest.disputeModuleData = abi.encode(_params); (IOracle.Response memory _response, IOracle.Dispute memory _dispute) = _getResponseAndDispute(oracle); bytes32 _requestId = _getId(mockRequest); - vm.warp(_params.bondEscalationDeadline + _params.tyingBuffer + 1); + _mockAndExpect( + address(oracle), abi.encodeCall(IOracle.disputeCreatedAt, (_getId(_dispute))), abi.encode(disputeCreatedAt) + ); + + vm.warp(disputeCreatedAt + _params.bondEscalationDeadline + _params.tyingBuffer + 1); bondEscalationModule.forTest_setBondEscalationStatus(_requestId, IBondEscalationModule.BondEscalationStatus.None); @@ -1370,7 +1430,7 @@ contract BondEscalationModule_Unit_SettleBondEscalation is BaseTest { public assumeFuzzable(address(_params.accountingExtension)) { - _params.bondEscalationDeadline = block.timestamp; + _params.bondEscalationDeadline = 500; _params.tyingBuffer = 1000; mockRequest.disputeModuleData = abi.encode(_params); @@ -1378,7 +1438,11 @@ contract BondEscalationModule_Unit_SettleBondEscalation is BaseTest { bytes32 _requestId = _getId(mockRequest); bytes32 _disputeId = _getId(_dispute); - vm.warp(_params.bondEscalationDeadline + _params.tyingBuffer + 1); + _mockAndExpect( + address(oracle), abi.encodeCall(IOracle.disputeCreatedAt, (_disputeId)), abi.encode(disputeCreatedAt) + ); + + vm.warp(disputeCreatedAt + _params.bondEscalationDeadline + _params.tyingBuffer + 1); bondEscalationModule.forTest_setBondEscalationStatus(_requestId, IBondEscalationModule.BondEscalationStatus.Active); bondEscalationModule.forTest_setEscalatedDispute(_requestId, _disputeId); @@ -1401,7 +1465,7 @@ contract BondEscalationModule_Unit_SettleBondEscalation is BaseTest { assumeFuzzable(address(_params.accountingExtension)) { _params.bondSize = 1000; - _params.bondEscalationDeadline = block.timestamp; + _params.bondEscalationDeadline = bondEscalationDeadline; _params.tyingBuffer = 1000; mockRequest.disputeModuleData = abi.encode(_params); @@ -1409,7 +1473,11 @@ contract BondEscalationModule_Unit_SettleBondEscalation is BaseTest { bytes32 _requestId = _getId(mockRequest); bytes32 _disputeId = _getId(_dispute); - vm.warp(_params.bondEscalationDeadline + _params.tyingBuffer + 1); + _mockAndExpect( + address(oracle), abi.encodeCall(IOracle.disputeCreatedAt, (_disputeId)), abi.encode(disputeCreatedAt) + ); + + vm.warp(disputeCreatedAt + _params.bondEscalationDeadline + _params.tyingBuffer + 1); bondEscalationModule.forTest_setBondEscalationStatus(_requestId, IBondEscalationModule.BondEscalationStatus.Active); bondEscalationModule.forTest_setEscalatedDispute(_requestId, _disputeId); @@ -1445,15 +1513,16 @@ contract BondEscalationModule_Unit_SettleBondEscalation is BaseTest { assumeFuzzable(address(_params.accountingExtension)) { _params.bondSize = 1000; - _params.bondEscalationDeadline = block.timestamp; + _params.bondEscalationDeadline = bondEscalationDeadline; _params.tyingBuffer = 1000; mockRequest.disputeModuleData = abi.encode(_params); - (IOracle.Response memory _response, IOracle.Dispute memory _dispute) = _getResponseAndDispute(oracle); + (IOracle.Response memory _response, IOracle.Dispute memory _dispute) = + _getResponseAndDispute(oracle, disputeCreatedAt); bytes32 _requestId = _getId(mockRequest); bytes32 _disputeId = _getId(_dispute); - vm.warp(_params.bondEscalationDeadline + _params.tyingBuffer + 1); + vm.warp(disputeCreatedAt + _params.bondEscalationDeadline + _params.tyingBuffer + 1); bondEscalationModule.forTest_setBondEscalationStatus(_requestId, IBondEscalationModule.BondEscalationStatus.Active); bondEscalationModule.forTest_setEscalatedDispute(_requestId, _disputeId); @@ -1470,7 +1539,7 @@ contract BondEscalationModule_Unit_SettleBondEscalation is BaseTest { ); // Check: is the event emitted? - vm.expectEmit(true, true, true, true, address(bondEscalationModule)); + vm.expectEmit(address(bondEscalationModule)); emit BondEscalationStatusUpdated(_requestId, _disputeId, IBondEscalationModule.BondEscalationStatus.DisputerLost); bondEscalationModule.settleBondEscalation(mockRequest, _response, _dispute); diff --git a/solidity/test/unit/modules/response/BondedResponseModule.t.sol b/solidity/test/unit/modules/response/BondedResponseModule.t.sol index 322e8207..5fb16cc0 100644 --- a/solidity/test/unit/modules/response/BondedResponseModule.t.sol +++ b/solidity/test/unit/modules/response/BondedResponseModule.t.sol @@ -28,6 +28,9 @@ contract BaseTest is Test, Helpers { IAccountingExtension public accounting = IAccountingExtension(makeAddr('Accounting')); // Base dispute window uint256 internal _baseDisputeWindow = 12 hours; + // Mock creation timestamps + uint256 public requestCreatedAt; + uint256 public responseCreatedAt; // Events event ResponseProposed(bytes32 indexed _requestId, IOracle.Response _response); @@ -42,6 +45,8 @@ contract BaseTest is Test, Helpers { // Avoid starting at 0 for time sensitive tests vm.warp(123_456); + requestCreatedAt = block.timestamp; + responseCreatedAt = requestCreatedAt + 30 seconds; bondedResponseModule = new BondedResponseModule(oracle); } @@ -117,7 +122,7 @@ contract BondedResponseModule_Unit_Propose is BaseTest { uint256 _disputeWindow, address _proposer ) public assumeFuzzable(_proposer) { - _deadline = bound(_deadline, block.timestamp + 1, type(uint248).max); + _deadline = bound(_deadline, 1, type(uint248).max); _disputeWindow = bound(_disputeWindow, 61, 365 days); _bondSize = bound(_bondSize, 0, type(uint248).max); @@ -128,6 +133,11 @@ contract BondedResponseModule_Unit_Propose is BaseTest { mockResponse.requestId = _requestId; mockResponse.proposer = _proposer; + // Mock and expect IOracle.requestCreatedAt to be called + _mockAndExpect( + address(oracle), abi.encodeCall(IOracle.requestCreatedAt, (_requestId)), abi.encode(requestCreatedAt) + ); + // Mock and expect IOracle.getResponseIds to be called _mockAndExpect(address(oracle), abi.encodeCall(IOracle.getResponseIds, _requestId), abi.encode(new bytes32[](0))); @@ -149,7 +159,7 @@ contract BondedResponseModule_Unit_Propose is BaseTest { uint256 _disputeWindow, address _proposer ) public { - _deadline = bound(_deadline, block.timestamp + 1, type(uint248).max); + _deadline = bound(_deadline, 1, type(uint248).max); _disputeWindow = bound(_disputeWindow, 61, 365 days); // Create and set some mock request data @@ -158,6 +168,10 @@ contract BondedResponseModule_Unit_Propose is BaseTest { mockResponse.requestId = _requestId; mockResponse.proposer = _proposer; + _mockAndExpect( + address(oracle), abi.encodeCall(IOracle.requestCreatedAt, (_requestId)), abi.encode(requestCreatedAt) + ); + // Mock and expect IOracle.getResponseIds to be called _mockAndExpect(address(oracle), abi.encodeCall(IOracle.getResponseIds, _requestId), abi.encode(new bytes32[](0))); @@ -188,7 +202,7 @@ contract BondedResponseModule_Unit_Propose is BaseTest { address _proposer ) public assumeFuzzable(_sender) assumeFuzzable(_proposer) { vm.assume(_sender != _proposer); - _deadline = bound(_deadline, block.timestamp + 1, type(uint248).max); + _deadline = bound(_deadline, 1, type(uint248).max); _disputeWindow = bound(_disputeWindow, 61, 365 days); _bondSize = bound(_bondSize, 0, type(uint248).max); @@ -199,6 +213,10 @@ contract BondedResponseModule_Unit_Propose is BaseTest { mockResponse.requestId = _requestId; mockResponse.proposer = _proposer; + _mockAndExpect( + address(oracle), abi.encodeCall(IOracle.requestCreatedAt, (_requestId)), abi.encode(requestCreatedAt) + ); + // Mock and expect IOracle.getResponseIds to be called _mockAndExpect(address(oracle), abi.encodeCall(IOracle.getResponseIds, _requestId), abi.encode(new bytes32[](0))); @@ -239,11 +257,14 @@ contract BondedResponseModule_Unit_FinalizeRequest is BaseTest { // Amount of blocks to wait before finalizing a response _disputeWindow = bound(_disputeWindow, 10, 90_000); // Last timestamp in which a response can be proposed - _deadline = bound(_deadline, 100_000, 365 days); + _deadline = bound(_deadline, 1, type(uint248).max); // Block in which the response was proposed - _responseCreationTimestamp = bound(_responseCreationTimestamp, _deadline - _disputeWindow + 1, _deadline - 1); + _responseCreationTimestamp = bound( + _responseCreationTimestamp, requestCreatedAt + _deadline - _disputeWindow + 1, requestCreatedAt + _deadline - 1 + ); // Block in which the request will be tried to be finalized - _finalizationTimestamp = bound(_finalizationTimestamp, _deadline, _responseCreationTimestamp + _disputeWindow - 1); + _finalizationTimestamp = + bound(_finalizationTimestamp, requestCreatedAt + _deadline, _responseCreationTimestamp + _disputeWindow - 1); // Check revert if deadline has not passed mockRequest.responseModuleData = abi.encode( @@ -262,10 +283,14 @@ contract BondedResponseModule_Unit_FinalizeRequest is BaseTest { address(oracle), abi.encodeCall(IOracle.allowedModule, (_getId(mockRequest), address(this))), abi.encode(false) ); + _mockAndExpect( + address(oracle), abi.encodeCall(IOracle.requestCreatedAt, (_getId(mockRequest))), abi.encode(requestCreatedAt) + ); + // Check: does it revert if it's too early to finalize? vm.expectRevert(IBondedResponseModule.BondedResponseModule_TooEarlyToFinalize.selector); - vm.warp(_deadline - 1); + vm.warp(requestCreatedAt + _deadline - 1); vm.prank(address(oracle)); bondedResponseModule.finalizeRequest(mockRequest, mockResponse, address(this)); @@ -299,7 +324,7 @@ contract BondedResponseModule_Unit_FinalizeRequest is BaseTest { _disputeWindow = bound(_disputeWindow, 61, 365 days); // Check correct calls are made if deadline has passed - _deadline = block.timestamp; + _deadline = bound(_deadline, 1, type(uint248).max); mockRequest.responseModuleData = abi.encode(accounting, _token, _bondSize, _deadline, _disputeWindow); mockResponse.requestId = _getId(mockRequest); mockResponse.proposer = _proposer; @@ -311,7 +336,7 @@ contract BondedResponseModule_Unit_FinalizeRequest is BaseTest { // Mock and expect IOracle.responseCreatedAt to be called _mockAndExpect( - address(oracle), abi.encodeCall(IOracle.responseCreatedAt, (_getId(mockResponse))), abi.encode(block.timestamp) + address(oracle), abi.encodeCall(IOracle.responseCreatedAt, (_getId(mockResponse))), abi.encode(responseCreatedAt) ); // Mock and expect IAccountingExtension.release to be called @@ -321,17 +346,23 @@ contract BondedResponseModule_Unit_FinalizeRequest is BaseTest { abi.encode(true) ); - vm.warp(block.timestamp + _disputeWindow); + vm.warp(responseCreatedAt + _disputeWindow); vm.prank(address(oracle)); bondedResponseModule.finalizeRequest(mockRequest, mockResponse, address(this)); } - function test_emitsEvent(IERC20 _token, uint256 _bondSize, uint256 _disputeWindow, address _proposer) public { + function test_emitsEvent( + IERC20 _token, + uint256 _bondSize, + uint256 _disputeWindow, + address _proposer, + uint256 _deadline + ) public { _disputeWindow = bound(_disputeWindow, 61, 365 days); // Check correct calls are made if deadline has passed - uint256 _deadline = block.timestamp; + _deadline = bound(_deadline, 1, type(uint248).max); mockRequest.responseModuleData = abi.encode(accounting, _token, _bondSize, _deadline, _disputeWindow); bytes32 _requestId = _getId(mockRequest); mockResponse.requestId = _requestId; @@ -342,6 +373,10 @@ contract BondedResponseModule_Unit_FinalizeRequest is BaseTest { address(oracle), abi.encodeCall(IOracle.allowedModule, (_requestId, address(this))), abi.encode(false) ); + _mockAndExpect( + address(oracle), abi.encodeCall(IOracle.requestCreatedAt, (_getId(mockRequest))), abi.encode(requestCreatedAt) + ); + // Mock and expect IOracle.responseCreatedAt to be called _mockAndExpect( address(oracle), abi.encodeCall(IOracle.responseCreatedAt, (_getId(mockResponse))), abi.encode(block.timestamp) @@ -358,7 +393,7 @@ contract BondedResponseModule_Unit_FinalizeRequest is BaseTest { vm.expectEmit(true, true, true, true, address(bondedResponseModule)); emit RequestFinalized({_requestId: _getId(mockRequest), _response: mockResponse, _finalizer: address(this)}); - vm.warp(block.timestamp + _disputeWindow); + vm.warp(requestCreatedAt + _deadline + _disputeWindow); vm.prank(address(oracle)); bondedResponseModule.finalizeRequest(mockRequest, mockResponse, address(this)); @@ -374,7 +409,7 @@ contract BondedResponseModule_Unit_FinalizeRequest is BaseTest { address _proposer, address _allowedModule ) public assumeFuzzable(_allowedModule) { - _deadline = bound(_deadline, block.timestamp + 1, type(uint248).max); + _deadline = bound(_deadline, 1, type(uint248).max); mockRequest.responseModuleData = abi.encode(accounting, _token, _bondSize, _deadline, _baseDisputeWindow); bytes32 _requestId = _getId(mockRequest); @@ -388,9 +423,7 @@ contract BondedResponseModule_Unit_FinalizeRequest is BaseTest { // Mock and expect IOracle.responseCreatedAt to be called _mockAndExpect( - address(oracle), - abi.encodeCall(IOracle.responseCreatedAt, (_getId(mockResponse))), - abi.encode(block.timestamp - _baseDisputeWindow) + address(oracle), abi.encodeCall(IOracle.responseCreatedAt, (_getId(mockResponse))), abi.encode(responseCreatedAt) ); // Mock and expect IAccountingExtension.release to be called @@ -422,6 +455,10 @@ contract BondedResponseModule_Unit_FinalizeRequest is BaseTest { // Mock and expect IOracle.allowedModule to be called _mockAndExpect(address(oracle), abi.encodeCall(IOracle.allowedModule, (_requestId, _finalizer)), abi.encode(false)); + _mockAndExpect( + address(oracle), abi.encodeCall(IOracle.requestCreatedAt, (_requestId)), abi.encode(requestCreatedAt) + ); + vm.expectRevert(IBondedResponseModule.BondedResponseModule_TooEarlyToFinalize.selector); vm.prank(address(oracle)); diff --git a/solidity/test/utils/Helpers.sol b/solidity/test/utils/Helpers.sol index 6bfc6591..44462aba 100644 --- a/solidity/test/utils/Helpers.sol +++ b/solidity/test/utils/Helpers.sol @@ -68,13 +68,22 @@ contract Helpers is DSTestPlus, TestConstants { internal returns (IOracle.Response memory _response, IOracle.Dispute memory _dispute) { + (_response, _dispute) = _getResponseAndDispute(_oracle, block.timestamp + 1 minutes); + } + + function _getResponseAndDispute( + IOracle _oracle, + uint256 _disputeCreatedAt + ) internal returns (IOracle.Response memory _response, IOracle.Dispute memory _dispute) { // Compute proper IDs _response = _getResponse(mockRequest, proposer); _dispute = _getDispute(mockRequest, _response); bytes32 _disputeId = _getId(_dispute); // Mock and expect IOracle.disputeCreatedAt to be called - _mockAndExpect(address(_oracle), abi.encodeCall(IOracle.disputeCreatedAt, (_disputeId)), abi.encode(1)); + _mockAndExpect( + address(_oracle), abi.encodeCall(IOracle.disputeCreatedAt, (_disputeId)), abi.encode(_disputeCreatedAt) + ); } /**