From 80d145f34097ab56b3ca9a9ea67baa3882c45657 Mon Sep 17 00:00:00 2001 From: 0xJabberwock <0xjabberwock@defi.sucks> Date: Fri, 29 Nov 2024 00:56:53 -0300 Subject: [PATCH] test(medusa): correct prop-6 --- test/invariants/handlers/BaseHandler.t.sol | 7 +++++-- test/invariants/handlers/HandlerOracle.t.sol | 17 ++++++++-------- .../properties/PropertyDisputer.t.sol | 20 ++++++++++++++----- .../properties/PropertyProposer.t.sol | 8 +++----- .../properties/PropertyRequester.t.sol | 2 +- 5 files changed, 33 insertions(+), 21 deletions(-) diff --git a/test/invariants/handlers/BaseHandler.t.sol b/test/invariants/handlers/BaseHandler.t.sol index a2ac9ef..67253ac 100644 --- a/test/invariants/handlers/BaseHandler.t.sol +++ b/test/invariants/handlers/BaseHandler.t.sol @@ -71,8 +71,11 @@ contract BaseHandler is Setup, Actors { bytes32 _requestId, uint256 _seed ) internal view returns (bytes32, IOracle.Response memory) { - bytes32 responseId = _ghost_activeResponses[_requestId][_seed % _ghost_activeResponses[_requestId].length]; - + bytes32[] storage responses = _ghost_activeResponses[_requestId]; + if (responses.length == 0) { + return (bytes32(0), IOracle.Response(address(0), bytes32(0), bytes(''))); + } + bytes32 responseId = responses[_seed % responses.length]; return (responseId, _ghost_responseData[responseId]); } diff --git a/test/invariants/handlers/HandlerOracle.t.sol b/test/invariants/handlers/HandlerOracle.t.sol index 9bf8c05..3129a09 100644 --- a/test/invariants/handlers/HandlerOracle.t.sol +++ b/test/invariants/handlers/HandlerOracle.t.sol @@ -31,26 +31,27 @@ contract HandlerOracle is BaseHandler { // Track response _ghost_activeResponses[requestId].push(responseId); _ghost_responseData[responseId] = response; + _ghost_bonds[proposer][requestId] += RESPONSE_BOND_SIZE; emit ResponseProposed(requestId, responseId); return responseId; } - function handleDisputeResponseOracle(uint256 _requestSeed, uint256 _actorSeed) external returns (bytes32) { + function handleDisputeResponseOracle( + uint256 _requestSeed, + uint256 _responseSeed, + uint256 _actorSeed + ) external returns (bytes32) { (bytes32 requestId, IOracle.Request memory request) = _getRandomRequest(_requestSeed); if (requestId == bytes32(0) || !_ghost_validRequests[requestId]) { return bytes32(0); } - if (_ghost_activeResponses[requestId].length == 0) return (bytes32(0)); - bytes32 responseId = _ghost_activeResponses[requestId][0]; - - IOracle.Response memory response = _ghost_responseData[responseId]; - if (_ghost_finalizedResponses[responseId]) return bytes32(0); + (bytes32 responseId, IOracle.Response memory response) = _getRandomActiveResponse(requestId, _responseSeed); + if (responseId == bytes32(0) || _ghost_finalizedResponses[responseId]) return bytes32(0); address disputer = _pickActor(_actorSeed); - uint256 bond = DISPUTE_BOND_SIZE; IOracle.Dispute memory dispute = IOracle.Dispute({disputer: disputer, proposer: response.proposer, requestId: requestId, responseId: responseId}); @@ -60,7 +61,7 @@ contract HandlerOracle is BaseHandler { // Track dispute _ghost_disputes[requestId].push(disputeId); _ghost_disputeData[disputeId] = dispute; - _ghost_bonds[disputer][requestId] = bond; + _ghost_bonds[disputer][requestId] += DISPUTE_BOND_SIZE; emit DisputeCreated(requestId, responseId, disputeId); diff --git a/test/invariants/properties/PropertyDisputer.t.sol b/test/invariants/properties/PropertyDisputer.t.sol index 530493a..ad0bec7 100644 --- a/test/invariants/properties/PropertyDisputer.t.sol +++ b/test/invariants/properties/PropertyDisputer.t.sol @@ -8,9 +8,13 @@ contract PropertyDisputer is HandlerParent { /// @custom:property-id 6 /// @custom:property A disputer can always dispute a response before the finalisation, if no previous dispute has been made function property_disputerCanAlwaysCreateDispute(uint256 _requestIdSeed, uint256 _responseIdSeed) public { - // Pick random Response + // Pick random request (bytes32 _requestId, IOracle.Request memory _requestData) = _getRandomRequest(_requestIdSeed); + if (_requestId == bytes32(0) || !_ghost_validRequests[_requestId]) return; + + // Pick random response (bytes32 _responseId, IOracle.Response memory _responseData) = _getRandomActiveResponse(_requestId, _responseIdSeed); + if (_responseId == bytes32(0)) return; IOracle.Dispute memory _disputeData = IOracle.Dispute({ disputer: msg.sender, @@ -23,21 +27,27 @@ contract PropertyDisputer is HandlerParent { _stakeGRT(DISPUTE_BOND_SIZE); _provisionGRT(DISPUTE_BOND_SIZE); + bytes32 _prevDisputeId = oracle.disputeOf(_responseId); + vm.prank(msg.sender); try oracle.disputeResponse(_requestData, _responseData, _disputeData) returns (bytes32 _disputeId) { // check if no previous dispute - assertEq(_ghost_disputes[_requestId].length, 0, 'property 6: new dispute duplicate'); + assertEq(_prevDisputeId, 0, 'property 6: new dispute duplicate'); // add to ghost disputes _ghost_disputes[_requestId].push(_disputeId); _ghost_disputeData[_disputeId] = _disputeData; + _ghost_bonds[msg.sender][_requestId] += DISPUTE_BOND_SIZE; + + emit DisputeCreated(_requestId, _responseId, _disputeId); } catch { // check that there was a previous dispute, or // outside of the dispute window, or - // + // request already finalized assertTrue( - _ghost_disputes[_responseId].length > 0 - || block.timestamp > oracle.responseCreatedAt(_responseId) + RESPONSE_DISPUTE_WINDOW, + _prevDisputeId != bytes32(0) + || block.timestamp > oracle.responseCreatedAt(_responseId) + RESPONSE_DISPUTE_WINDOW + || oracle.finalizedAt(_requestId) != 0, 'property 6: fails but no previous active dispute' ); } diff --git a/test/invariants/properties/PropertyProposer.t.sol b/test/invariants/properties/PropertyProposer.t.sol index f247d8e..3c818a5 100644 --- a/test/invariants/properties/PropertyProposer.t.sol +++ b/test/invariants/properties/PropertyProposer.t.sol @@ -9,7 +9,7 @@ contract PropertyProposer is HandlerParent { /// @custom:property A proposer can always propose an answer before the deadline, if no response has been submitted /// @custom:property-id 4 /// @custom:property A proposer can always propose an answer before the deadline, if previous response is disputed and has staked - function property_proposeBeforeDeadlineAndNoAnswer(uint256 _requestIdSeed, bytes calldata _response) public { + function property_proposerProposeBeforeDeadlineAndNoAnswer(uint256 _requestIdSeed, bytes calldata _response) public { // Pick random request (bytes32 requestId, IOracle.Request memory requestData) = _getRandomRequest(_requestIdSeed); if (requestId == bytes32(0) || !_ghost_validRequests[requestId]) return; @@ -17,9 +17,6 @@ contract PropertyProposer is HandlerParent { // Build response data IOracle.Response memory responseData = IOracle.Response(msg.sender, requestId, _response); // abi.encode(_blockNumber)? - // Calculate response ID using same logic as Oracle - bytes32 responseId = keccak256(abi.encode(responseData)); - // Stake some GRT in Horizon _stakeGRT(RESPONSE_BOND_SIZE); // Provision some GRT in Horizon @@ -27,10 +24,11 @@ contract PropertyProposer is HandlerParent { // Propose response vm.prank(msg.sender); - try oracle.proposeResponse(requestData, responseData) { + try oracle.proposeResponse(requestData, responseData) returns (bytes32 responseId) { // Track response _ghost_activeResponses[requestId].push(responseId); _ghost_responseData[responseId] = responseData; + _ghost_bonds[msg.sender][requestId] += RESPONSE_BOND_SIZE; emit ResponseProposed(requestId, responseId); } catch { diff --git a/test/invariants/properties/PropertyRequester.t.sol b/test/invariants/properties/PropertyRequester.t.sol index 6a8e88b..7cda835 100644 --- a/test/invariants/properties/PropertyRequester.t.sol +++ b/test/invariants/properties/PropertyRequester.t.sol @@ -12,7 +12,7 @@ contract PropertyRequester is HandlerParent { /// @custom:property Requester can always create a request as long as the same chainId/epoch isn't already finalized with response /// @custom:property-id 2 /// @custom:property Requester can always create a request as long as there is no other active request per the same chainId/epoch - function property_canAlwaysCreateRequest(uint256 _epoch, uint256 _chainIdSeed) external { + function property_requesterCanAlwaysCreateRequest(uint256 _epoch, uint256 _chainIdSeed) external { _epoch = bound(_epoch, START_EPOCH, block.timestamp); string memory chainId = _getRandomChain(_chainIdSeed);