diff --git a/solidity/contracts/Module.sol b/solidity/contracts/Module.sol index 13e1696e..f8d679bf 100644 --- a/solidity/contracts/Module.sol +++ b/solidity/contracts/Module.sol @@ -3,10 +3,11 @@ pragma solidity ^0.8.19; import {IModule} from '../interfaces/IModule.sol'; import {IOracle} from '../interfaces/IOracle.sol'; + import {Validator} from './Validator.sol'; -abstract contract Module is Validator, IModule { - constructor(IOracle _oracle) payable Validator(_oracle) {} +abstract contract Module is IModule, Validator { + constructor(IOracle _oracle) Validator(_oracle) {} /** * @notice Checks that the caller is the oracle diff --git a/solidity/contracts/Oracle.sol b/solidity/contracts/Oracle.sol index e43565d1..a41333d1 100644 --- a/solidity/contracts/Oracle.sol +++ b/solidity/contracts/Oracle.sol @@ -9,6 +9,7 @@ import {IFinalityModule} from '../interfaces/modules/finality/IFinalityModule.so import {IRequestModule} from '../interfaces/modules/request/IRequestModule.sol'; import {IResolutionModule} from '../interfaces/modules/resolution/IResolutionModule.sol'; import {IResponseModule} from '../interfaces/modules/response/IResponseModule.sol'; +import {ValidatorLib} from '../lib/ValidatorLib.sol'; contract Oracle is IOracle { /// @inheritdoc IOracle @@ -104,7 +105,12 @@ contract Oracle is IOracle { Request calldata _request, Response calldata _response ) external returns (bytes32 _responseId) { - _responseId = _validateResponse(_request, _response); + bytes32 _requestId = ValidatorLib._getId(_request); + (_responseId) = ValidatorLib._validateResponse(_request, _response); + + if (requestCreatedAt[_requestId] == 0) { + revert Oracle_InvalidRequest(); + } // The caller must be the proposer, unless the response is coming from a dispute module if (msg.sender != _response.proposer && msg.sender != address(_request.disputeModule)) { @@ -134,13 +140,18 @@ contract Oracle is IOracle { Response calldata _response, Dispute calldata _dispute ) external returns (bytes32 _disputeId) { - _disputeId = _validateDispute(_request, _response, _dispute); + bytes32 _responseId; + (_responseId, _disputeId) = ValidatorLib._validateResponseAndDispute(_request, _response, _dispute); + + if (responseCreatedAt[_responseId] == 0) { + revert Oracle_InvalidResponse(); + } if (_dispute.proposer != _response.proposer) { revert Oracle_InvalidDisputeBody(); } - if (_dispute.disputer != msg.sender || requestCreatedAt[_dispute.requestId] == 0) { + if (_dispute.disputer != msg.sender) { revert Oracle_InvalidDisputeBody(); } @@ -164,7 +175,11 @@ contract Oracle is IOracle { /// @inheritdoc IOracle function escalateDispute(Request calldata _request, Response calldata _response, Dispute calldata _dispute) external { - bytes32 _disputeId = _validateDispute(_request, _response, _dispute); + (, bytes32 _disputeId) = ValidatorLib._validateResponseAndDispute(_request, _response, _dispute); + + if (disputeCreatedAt[_disputeId] == 0) { + revert Oracle_InvalidDispute(); + } if (disputeOf[_dispute.responseId] != _disputeId) { revert Oracle_InvalidDisputeId(_disputeId); @@ -190,7 +205,11 @@ contract Oracle is IOracle { /// @inheritdoc IOracle function resolveDispute(Request calldata _request, Response calldata _response, Dispute calldata _dispute) external { - bytes32 _disputeId = _validateDispute(_request, _response, _dispute); + (, bytes32 _disputeId) = ValidatorLib._validateResponseAndDispute(_request, _response, _dispute); + + if (disputeCreatedAt[_disputeId] == 0) { + revert Oracle_InvalidDispute(); + } if (disputeOf[_dispute.responseId] != _disputeId) { revert Oracle_InvalidDisputeId(_disputeId); @@ -218,7 +237,11 @@ contract Oracle is IOracle { Dispute calldata _dispute, DisputeStatus _status ) external { - bytes32 _disputeId = _validateDispute(_request, _response, _dispute); + (, bytes32 _disputeId) = ValidatorLib._validateResponseAndDispute(_request, _response, _dispute); + + if (disputeCreatedAt[_disputeId] == 0) { + revert Oracle_InvalidDispute(); + } if (disputeOf[_dispute.responseId] != _disputeId) { revert Oracle_InvalidDisputeId(_disputeId); @@ -328,7 +351,12 @@ contract Oracle is IOracle { * @return _requestId The id of the finalized request */ function _finalizeWithoutResponse(IOracle.Request calldata _request) internal view returns (bytes32 _requestId) { - _requestId = keccak256(abi.encode(_request)); + _requestId = ValidatorLib._getId(_request); + + if (requestCreatedAt[_requestId] == 0) { + revert Oracle_InvalidRequest(); + } + bytes32[] memory _responses = getResponseIds(_requestId); uint256 _responsesAmount = _responses.length; @@ -362,7 +390,12 @@ contract Oracle is IOracle { IOracle.Request calldata _request, IOracle.Response calldata _response ) internal returns (bytes32 _requestId, bytes32 _responseId) { - _responseId = _validateResponse(_request, _response); + _responseId = ValidatorLib._validateResponse(_request, _response); + + if (responseCreatedAt[_responseId] == 0) { + revert Oracle_InvalidResponse(); + } + _requestId = _response.requestId; if (!_matchBytes(_responseId, _responseIds[_requestId], 32)) { revert Oracle_InvalidFinalizedResponse(); @@ -411,41 +444,4 @@ contract Oracle is IOracle { emit RequestCreated(_requestId, _request, _ipfsHash, block.number); } - - /** - * @notice Validates the correctness of a request-response pair - * - * @param _request The request to compute the id for - * @param _response The response to compute the id for - * @return _responseId The id the response - */ - function _validateResponse( - Request calldata _request, - Response calldata _response - ) internal pure returns (bytes32 _responseId) { - bytes32 _requestId = keccak256(abi.encode(_request)); - _responseId = keccak256(abi.encode(_response)); - if (_response.requestId != _requestId) revert Oracle_InvalidResponseBody(); - } - - /** - * @notice Validates the correctness of a request-response-dispute triplet - * - * @param _request The request to compute the id for - * @param _response The response to compute the id for - * @param _dispute The dispute to compute the id for - * @return _disputeId The id the dispute - */ - function _validateDispute( - Request calldata _request, - Response calldata _response, - Dispute calldata _dispute - ) internal pure returns (bytes32 _disputeId) { - bytes32 _requestId = keccak256(abi.encode(_request)); - bytes32 _responseId = keccak256(abi.encode(_response)); - _disputeId = keccak256(abi.encode(_dispute)); - - if (_dispute.requestId != _requestId || _dispute.responseId != _responseId) revert Oracle_InvalidDisputeBody(); - if (_response.requestId != _requestId) revert Oracle_InvalidResponseBody(); - } } diff --git a/solidity/contracts/Validator.sol b/solidity/contracts/Validator.sol index d2694d12..00b0d554 100644 --- a/solidity/contracts/Validator.sol +++ b/solidity/contracts/Validator.sol @@ -1,25 +1,26 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.19; -import {IOracle} from '../interfaces/IOracle.sol'; -import {IValidator} from '../interfaces/IValidator.sol'; +import {IOracle, IValidator} from '../interfaces/IValidator.sol'; -contract Validator is IValidator { +import {ValidatorLib} from '../lib/ValidatorLib.sol'; + +abstract contract Validator is IValidator { /// @inheritdoc IValidator IOracle public immutable ORACLE; - constructor(IOracle _oracle) payable { + constructor(IOracle _oracle) { ORACLE = _oracle; } - /** * @notice Computes the id a given request * * @param _request The request to compute the id for * @return _id The id the request */ + function _getId(IOracle.Request calldata _request) internal pure returns (bytes32 _id) { - _id = keccak256(abi.encode(_request)); + _id = ValidatorLib._getId(_request); } /** @@ -29,7 +30,7 @@ contract Validator is IValidator { * @return _id The id the response */ function _getId(IOracle.Response calldata _response) internal pure returns (bytes32 _id) { - _id = keccak256(abi.encode(_response)); + _id = ValidatorLib._getId(_response); } /** @@ -39,7 +40,7 @@ contract Validator is IValidator { * @return _id The id the dispute */ function _getId(IOracle.Dispute calldata _dispute) internal pure returns (bytes32 _id) { - _id = keccak256(abi.encode(_dispute)); + _id = ValidatorLib._getId(_dispute); } /** @@ -53,10 +54,8 @@ contract Validator is IValidator { IOracle.Request calldata _request, IOracle.Response calldata _response ) internal view returns (bytes32 _responseId) { - bytes32 _requestId = _getId(_request); - _responseId = _getId(_response); + _responseId = ValidatorLib._validateResponse(_request, _response); - if (_response.requestId != _requestId) revert Validator_InvalidResponseBody(); if (ORACLE.responseCreatedAt(_responseId) == 0) revert Validator_InvalidResponse(); } @@ -71,10 +70,8 @@ contract Validator is IValidator { IOracle.Request calldata _request, IOracle.Dispute calldata _dispute ) internal view returns (bytes32 _disputeId) { - bytes32 _requestId = _getId(_request); - _disputeId = _getId(_dispute); + _disputeId = ValidatorLib._validateDispute(_request, _dispute); - if (_dispute.requestId != _requestId) revert Validator_InvalidDisputeBody(); if (ORACLE.disputeCreatedAt(_disputeId) == 0) revert Validator_InvalidDispute(); } @@ -89,10 +86,8 @@ contract Validator is IValidator { IOracle.Response calldata _response, IOracle.Dispute calldata _dispute ) internal view returns (bytes32 _disputeId) { - bytes32 _responseId = _getId(_response); - _disputeId = _getId(_dispute); + _disputeId = ValidatorLib._validateDispute(_response, _dispute); - if (_dispute.responseId != _responseId) revert Validator_InvalidDisputeBody(); if (ORACLE.disputeCreatedAt(_disputeId) == 0) revert Validator_InvalidDispute(); } @@ -110,12 +105,8 @@ contract Validator is IValidator { IOracle.Response calldata _response, IOracle.Dispute calldata _dispute ) internal view returns (bytes32 _responseId, bytes32 _disputeId) { - bytes32 _requestId = _getId(_request); - _responseId = _getId(_response); - _disputeId = _getId(_dispute); + (_responseId, _disputeId) = ValidatorLib._validateResponseAndDispute(_request, _response, _dispute); - if (_response.requestId != _requestId) revert Validator_InvalidResponseBody(); - if (_dispute.requestId != _requestId || _dispute.responseId != _responseId) revert Validator_InvalidDisputeBody(); if (ORACLE.disputeCreatedAt(_disputeId) == 0) revert Validator_InvalidDispute(); } } diff --git a/solidity/interfaces/IOracle.sol b/solidity/interfaces/IOracle.sol index afb4b9d8..4bf9af5b 100644 --- a/solidity/interfaces/IOracle.sol +++ b/solidity/interfaces/IOracle.sol @@ -148,6 +148,21 @@ interface IOracle { */ error Oracle_InvalidDisputeBody(); + /** + * @notice Thrown when the request provided does not exist + */ + error Oracle_InvalidRequest(); + + /** + * @notice Thrown when the response provided does not exist + */ + error Oracle_InvalidResponse(); + + /** + * @notice Thrown when the dispute provided does not exist + */ + error Oracle_InvalidDispute(); + /*/////////////////////////////////////////////////////////////// ENUMS //////////////////////////////////////////////////////////////*/ diff --git a/solidity/interfaces/IValidator.sol b/solidity/interfaces/IValidator.sol index 3b1e1981..80ee18db 100644 --- a/solidity/interfaces/IValidator.sol +++ b/solidity/interfaces/IValidator.sol @@ -12,16 +12,6 @@ interface IValidator { ERRORS //////////////////////////////////////////////////////////////*/ - /** - * @notice Thrown when the response provided does not match the request - */ - error Validator_InvalidResponseBody(); - - /** - * @notice Thrown when the dispute provided does not match the request or response - */ - error Validator_InvalidDisputeBody(); - /** * @notice Thrown when the response provided does not exist */ @@ -31,15 +21,12 @@ interface IValidator { * @notice Thrown when the dispute provided does not exist */ error Validator_InvalidDispute(); - /*/////////////////////////////////////////////////////////////// VARIABLES //////////////////////////////////////////////////////////////*/ /** - * @notice Returns the address of the oracle - * - * @return _oracle The address of the oracle + * @notice The oracle contract */ function ORACLE() external view returns (IOracle _oracle); } diff --git a/solidity/lib/ValidatorLib.sol b/solidity/lib/ValidatorLib.sol new file mode 100644 index 00000000..8c694cae --- /dev/null +++ b/solidity/lib/ValidatorLib.sol @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +import {IOracle} from '../interfaces/IOracle.sol'; + +library ValidatorLib { + /** + * @notice Thrown when the response provided does not match the request + */ + error ValidatorLib_InvalidResponseBody(); + + /** + * @notice Thrown when the dispute provided does not match the request or response + */ + error ValidatorLib_InvalidDisputeBody(); + + /** + * @notice Computes the id a given request + * + * @param _request The request to compute the id for + * @return _id The id the request + */ + function _getId(IOracle.Request calldata _request) public pure returns (bytes32 _id) { + _id = keccak256(abi.encode(_request)); + } + + /** + * @notice Computes the id a given response + * + * @param _response The response to compute the id for + * @return _id The id the response + */ + function _getId(IOracle.Response calldata _response) public pure returns (bytes32 _id) { + _id = keccak256(abi.encode(_response)); + } + + /** + * @notice Computes the id a given dispute + * + * @param _dispute The dispute to compute the id for + * @return _id The id the dispute + */ + function _getId(IOracle.Dispute calldata _dispute) public pure returns (bytes32 _id) { + _id = keccak256(abi.encode(_dispute)); + } + + /** + * @notice Validates the correctness and existance of a request-response pair + * + * @param _request The request to compute the id for + * @param _response The response to compute the id for + * @return _responseId The id the response + */ + function _validateResponse( + IOracle.Request calldata _request, + IOracle.Response calldata _response + ) public pure returns (bytes32 _responseId) { + bytes32 _requestId = _getId(_request); + _responseId = _getId(_response); + + if (_response.requestId != _requestId) revert ValidatorLib_InvalidResponseBody(); + } + + /** + * @notice Validates the correctness of a request-dispute pair + * + * @param _request The request to compute the id for + * @param _dispute The dispute to compute the id for + * @return _disputeId The id the dispute + */ + function _validateDispute( + IOracle.Request calldata _request, + IOracle.Dispute calldata _dispute + ) public pure returns (bytes32 _disputeId) { + bytes32 _requestId = _getId(_request); + _disputeId = _getId(_dispute); + + if (_dispute.requestId != _requestId) revert ValidatorLib_InvalidDisputeBody(); + } + + /** + * @notice Validates the correctness of a response-dispute pair + * + * @param _response The response to compute the id for + * @param _dispute The dispute to compute the id for + * @return _disputeId The id the dispute + */ + function _validateDispute( + IOracle.Response calldata _response, + IOracle.Dispute calldata _dispute + ) public pure returns (bytes32 _disputeId) { + bytes32 _responseId = _getId(_response); + _disputeId = _getId(_dispute); + + if (_dispute.responseId != _responseId) revert ValidatorLib_InvalidDisputeBody(); + } + + /** + * @notice Validates the correctness of a request-response-dispute triplet + * + * @param _request The request to compute the id for + * @param _response The response to compute the id for + * @param _dispute The dispute to compute the id for + * @return _responseId The id the response + * @return _disputeId The id the dispute + */ + function _validateResponseAndDispute( + IOracle.Request calldata _request, + IOracle.Response calldata _response, + IOracle.Dispute calldata _dispute + ) public pure returns (bytes32 _responseId, bytes32 _disputeId) { + bytes32 _requestId = _getId(_request); + _responseId = _getId(_response); + _disputeId = _getId(_dispute); + + if (_response.requestId != _requestId) revert ValidatorLib_InvalidResponseBody(); + if (_dispute.requestId != _requestId || _dispute.responseId != _responseId) { + revert ValidatorLib_InvalidDisputeBody(); + } + } +} diff --git a/solidity/test/lib/ValidatorLib.t.sol b/solidity/test/lib/ValidatorLib.t.sol new file mode 100644 index 00000000..b40f2463 --- /dev/null +++ b/solidity/test/lib/ValidatorLib.t.sol @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +import 'forge-std/Test.sol'; + +import {IOracle} from '../../interfaces/IOracle.sol'; + +import {IDisputeModule} from '../../interfaces/modules/dispute/IDisputeModule.sol'; + +import {IFinalityModule} from '../../interfaces/modules/finality/IFinalityModule.sol'; +import {IRequestModule} from '../../interfaces/modules/request/IRequestModule.sol'; +import {IResolutionModule} from '../../interfaces/modules/resolution/IResolutionModule.sol'; +import {IResponseModule} from '../../interfaces/modules/response/IResponseModule.sol'; + +import {ValidatorLib} from '../../lib/ValidatorLib.sol'; +import {Helpers} from '../utils/Helpers.sol'; + +/** + * @title ValidatorLib Unit tests + */ +contract BaseTest is Test, Helpers { + // Mock Oracle + IOracle public oracle = IOracle(_mockContract('oracle')); + + // Mock modules + IRequestModule public requestModule = IRequestModule(_mockContract('requestModule')); + IResponseModule public responseModule = IResponseModule(_mockContract('responseModule')); + IDisputeModule public disputeModule = IDisputeModule(_mockContract('disputeModule')); + IResolutionModule public resolutionModule = IResolutionModule(_mockContract('resolutionModule')); + IFinalityModule public finalityModule = IFinalityModule(_mockContract('finalityModule')); + + function setUp() public virtual { + mockRequest.requestModule = address(requestModule); + mockRequest.responseModule = address(responseModule); + mockRequest.disputeModule = address(disputeModule); + mockRequest.resolutionModule = address(resolutionModule); + mockRequest.finalityModule = address(finalityModule); + + mockResponse.requestId = ValidatorLib._getId(mockRequest); + mockDispute.requestId = mockResponse.requestId; + mockDispute.responseId = ValidatorLib._getId(mockResponse); + + vm.mockCall( + address(oracle), + abi.encodeWithSelector(IOracle.responseCreatedAt.selector, _getId(mockResponse)), + abi.encode(1000) + ); + vm.mockCall( + address(oracle), abi.encodeWithSelector(IOracle.disputeCreatedAt.selector, _getId(mockDispute)), abi.encode(1000) + ); + } +} + +contract ValidatorLibGetIds is BaseTest { + function test_getId_request() public { + bytes32 _requestId = ValidatorLib._getId(mockRequest); + assertEq(_requestId, keccak256(abi.encode(mockRequest))); + } + + function test_getId_response() public { + bytes32 _responseId = ValidatorLib._getId(mockResponse); + assertEq(_responseId, keccak256(abi.encode(mockResponse))); + } + + function test_getId_dispute() public { + bytes32 _disputeId = ValidatorLib._getId(mockDispute); + assertEq(_disputeId, keccak256(abi.encode(mockDispute))); + } +} + +contract ValidatorLibValidateResponse is BaseTest { + function test_validateResponse() public { + bytes32 _responseId = ValidatorLib._validateResponse(mockRequest, mockResponse); + assertEq(_responseId, keccak256(abi.encode(mockResponse))); + } + + function test__validateResponse_InvalidResponseBody() public { + IOracle.Response memory _response = mockResponse; + _response.requestId = bytes32('invalid'); + vm.expectRevert(ValidatorLib.ValidatorLib_InvalidResponseBody.selector); + ValidatorLib._validateResponse(mockRequest, _response); + } +} + +contract ValidatorLibValidateDisputeRequest is BaseTest { + function test_validateDispute() public { + bytes32 _disputeId = ValidatorLib._validateDispute(mockRequest, mockDispute); + assertEq(_disputeId, keccak256(abi.encode(mockDispute))); + } + + function test_validateDispute_InvalidDisputeBody() public { + IOracle.Dispute memory _dispute = mockDispute; + _dispute.requestId = bytes32('invalid'); + vm.expectRevert(ValidatorLib.ValidatorLib_InvalidDisputeBody.selector); + ValidatorLib._validateDispute(mockRequest, _dispute); + } +} + +contract ValidatorLibValidateDisputeResponse is BaseTest { + function test_validateDispute() public { + bytes32 _disputeId = ValidatorLib._validateDispute(mockResponse, mockDispute); + assertEq(_disputeId, keccak256(abi.encode(mockDispute))); + } + + function test_validateDispute_InvalidDisputeBody() public { + IOracle.Dispute memory _dispute = mockDispute; + _dispute.responseId = bytes32('invalid'); + vm.expectRevert(ValidatorLib.ValidatorLib_InvalidDisputeBody.selector); + ValidatorLib._validateDispute(mockResponse, _dispute); + } +} + +contract ValidatorLib_ValidateResponseAndDispute is BaseTest { + function test_validateResponseAndDispute() public { + (bytes32 _responseId, bytes32 _disputeId) = + ValidatorLib._validateResponseAndDispute(mockRequest, mockResponse, mockDispute); + assertEq(_responseId, keccak256(abi.encode(mockResponse))); + assertEq(_disputeId, keccak256(abi.encode(mockDispute))); + } + + function test_validateResponseAndDispute_InvalidResponseBody() public { + IOracle.Response memory _response = mockResponse; + _response.requestId = bytes32('invalid'); + vm.expectRevert(ValidatorLib.ValidatorLib_InvalidResponseBody.selector); + ValidatorLib._validateResponseAndDispute(mockRequest, _response, mockDispute); + } + + function test_validateResponseAndDispute_InvalidDisputeBody() public { + IOracle.Dispute memory _dispute = mockDispute; + _dispute.requestId = bytes32('invalid'); + vm.expectRevert(ValidatorLib.ValidatorLib_InvalidDisputeBody.selector); + ValidatorLib._validateResponseAndDispute(mockRequest, mockResponse, _dispute); + } + + function test_validateResponseAndDispute_InvalidDisputeBodyResponseId() public { + IOracle.Dispute memory _dispute = mockDispute; + _dispute.responseId = bytes32('invalid'); + vm.expectRevert(ValidatorLib.ValidatorLib_InvalidDisputeBody.selector); + ValidatorLib._validateResponseAndDispute(mockRequest, mockResponse, _dispute); + } +} diff --git a/solidity/test/unit/Oracle.t.sol b/solidity/test/unit/Oracle.t.sol index dd79c82b..504c3d11 100644 --- a/solidity/test/unit/Oracle.t.sol +++ b/solidity/test/unit/Oracle.t.sol @@ -16,6 +16,8 @@ import {IResponseModule} from '../../interfaces/modules/response/IResponseModule import {Oracle} from '../../contracts/Oracle.sol'; import {Helpers} from '../utils/Helpers.sol'; +import {ValidatorLib} from '../../lib/ValidatorLib.sol'; + /** * @notice Harness to deploy and test Oracle */ @@ -54,6 +56,14 @@ contract MockOracle is Oracle { requestCreatedAt[_requestId] = _requestCreatedAt; } + function mock_setResponseCreatedAt(bytes32 _responseId, uint128 _responseCreatedAt) external { + responseCreatedAt[_responseId] = _responseCreatedAt; + } + + function mock_setDisputeCreatedAt(bytes32 _disputeId, uint128 _disputeCreatedAt) external { + disputeCreatedAt[_disputeId] = _disputeCreatedAt; + } + function mock_setTotalRequestCount(uint256 _totalRequestCount) external { totalRequestCount = _totalRequestCount; } @@ -409,6 +419,9 @@ contract Oracle_Unit_ProposeResponse is BaseTest { // Compute the response ID bytes32 _responseId = _getId(mockResponse); + // Set the request creation time + oracle.mock_setRequestCreatedAt(_requestId, uint128(block.number)); + // Mock and expect the responseModule propose call: _mockAndExpect( address(responseModule), @@ -446,12 +459,23 @@ contract Oracle_Unit_ProposeResponse is BaseTest { assertEq(_responseIds[1], _secondResponseId); } + function test_proposeResponse_revertsIfInvalidRequest() public { + // Check: revert? + vm.expectRevert(IOracle.Oracle_InvalidRequest.selector); + + // Test: try to propose a response with an invalid request + vm.prank(proposer); + oracle.proposeResponse(mockRequest, mockResponse); + } + /** * @notice Revert if the caller is not the proposer nor the dispute module */ function test_proposeResponse_revertsIfInvalidCaller(address _caller) public { vm.assume(_caller != proposer && _caller != address(disputeModule)); + oracle.mock_setRequestCreatedAt(_getId(mockRequest), uint128(block.number)); + // Check: revert? vm.expectRevert(IOracle.Oracle_InvalidResponseBody.selector); @@ -464,6 +488,9 @@ contract Oracle_Unit_ProposeResponse is BaseTest { * @notice Revert if the response has been already proposed */ function test_proposeResponse_revertsIfDuplicateResponse() public { + // Set the request creation time + oracle.mock_setRequestCreatedAt(_getId(mockRequest), uint128(block.number)); + // Test: propose a response vm.prank(proposer); oracle.proposeResponse(mockRequest, mockResponse); @@ -485,6 +512,7 @@ contract Oracle_Unit_ProposeResponse is BaseTest { // Set the finalization time bytes32 _requestId = _getId(mockRequest); oracle.mock_setFinalizedAt(_requestId, _finalizedAt); + oracle.mock_setRequestCreatedAt(_requestId, uint128(block.number)); // Check: Reverts if already finalized? vm.expectRevert(abi.encodeWithSelector(IOracle.Oracle_AlreadyFinalized.selector, (_requestId))); @@ -503,7 +531,7 @@ contract Oracle_Unit_DisputeResponse is BaseTest { _responseId = _getId(mockResponse); _disputeId = _getId(mockDispute); - oracle.mock_setRequestCreatedAt(_getId(mockRequest), uint128(block.number)); + oracle.mock_setResponseCreatedAt(_responseId, uint128(block.number)); } /** @@ -541,7 +569,7 @@ contract Oracle_Unit_DisputeResponse is BaseTest { */ function test_disputeResponse_revertIfProposerIsNotValid(address _otherProposer) public { vm.assume(_otherProposer != proposer); - oracle.mock_setRequestCreatedAt(_getId(mockRequest), 0); + oracle.mock_setRequestCreatedAt(_getId(mockRequest), uint128(block.number)); // Check: revert? vm.expectRevert(IOracle.Oracle_InvalidDisputeBody.selector); @@ -554,13 +582,13 @@ contract Oracle_Unit_DisputeResponse is BaseTest { } /** - * @notice Reverts if the request doesn't exist + * @notice Reverts if the response doesn't exist */ - function test_disputeResponse_revertIfInvalidRequest() public { - oracle.mock_setRequestCreatedAt(_getId(mockRequest), 0); + function test_disputeResponse_revertIfInvalidResponse() public { + oracle.mock_setResponseCreatedAt(_getId(mockResponse), 0); // Check: revert? - vm.expectRevert(IOracle.Oracle_InvalidDisputeBody.selector); + vm.expectRevert(IOracle.Oracle_InvalidResponse.selector); // Test: try to dispute the response vm.prank(disputer); @@ -602,7 +630,7 @@ contract Oracle_Unit_UpdateDisputeStatus is BaseTest { */ function test_updateDisputeStatus() public { bytes32 _requestId = _getId(mockRequest); - oracle.mock_setRequestCreatedAt(_getId(mockRequest), uint128(block.number)); + oracle.mock_setDisputeCreatedAt(_getId(mockDispute), uint128(block.number)); // Try every initial status for (uint256 _previousStatus; _previousStatus < uint256(type(IOracle.DisputeStatus).max); _previousStatus++) { @@ -647,6 +675,7 @@ contract Oracle_Unit_UpdateDisputeStatus is BaseTest { // Setting a random dispute id, not matching the mockDispute oracle.mock_setDisputeOf(_getId(mockResponse), _randomId); + oracle.mock_setDisputeCreatedAt(_disputeId, uint128(block.number)); // Check: revert? vm.expectRevert(abi.encodeWithSelector(IOracle.Oracle_InvalidDisputeId.selector, _disputeId)); @@ -663,9 +692,12 @@ contract Oracle_Unit_UpdateDisputeStatus is BaseTest { // 0 to 3 status, fuzzed _newStatus = bound(_newStatus, 0, 3); + bytes32 _disputeId = _getId(mockDispute); + bytes32 _responseId = _getId(mockResponse); + // Mock the dispute - oracle.mock_setDisputeOf(_getId(mockResponse), _getId(mockDispute)); - oracle.mock_setRequestCreatedAt(_getId(mockRequest), uint128(block.number)); + oracle.mock_setDisputeOf(_responseId, _disputeId); + oracle.mock_setDisputeCreatedAt(_disputeId, uint128(block.number)); // Check: revert? vm.expectRevert(abi.encodeWithSelector(IOracle.Oracle_NotDisputeOrResolutionModule.selector, proposer)); @@ -674,6 +706,22 @@ contract Oracle_Unit_UpdateDisputeStatus is BaseTest { vm.prank(proposer); oracle.updateDisputeStatus(mockRequest, mockResponse, mockDispute, IOracle.DisputeStatus(_newStatus)); } + + /** + * @notice If the dispute does not exist, the call should revert + */ + function test_updateDisputeStatus_revertsIfInvalidDispute() public { + bytes32 _disputeId = _getId(mockDispute); + + oracle.mock_setDisputeCreatedAt(_disputeId, 0); + + // Check: revert? + vm.expectRevert(abi.encodeWithSelector(IOracle.Oracle_InvalidDispute.selector)); + + // Test: try to update the status + vm.prank(address(resolutionModule)); + oracle.updateDisputeStatus(mockRequest, mockResponse, mockDispute, IOracle.DisputeStatus.Active); + } } contract Oracle_Unit_ResolveDispute is BaseTest { @@ -683,6 +731,10 @@ contract Oracle_Unit_ResolveDispute is BaseTest { function test_resolveDispute_callsResolutionModule() public { // Mock the dispute bytes32 _disputeId = _getId(mockDispute); + oracle.mock_setRequestCreatedAt(_getId(mockRequest), uint128(block.number)); + oracle.mock_setResponseCreatedAt(_getId(mockResponse), uint128(block.number)); + oracle.mock_setDisputeCreatedAt(_disputeId, uint128(block.number)); + oracle.mock_setDisputeOf(_getId(mockResponse), _disputeId); oracle.mock_setDisputeStatus(_disputeId, IOracle.DisputeStatus.Active); @@ -704,7 +756,9 @@ contract Oracle_Unit_ResolveDispute is BaseTest { /** * @notice Test the revert when the function is called with an non-existent dispute id */ - function test_resolveDispute_revertsIfInvalidDispute() public { + function test_resolveDispute_revertsIfInvalidDisputeId() public { + oracle.mock_setDisputeCreatedAt(_getId(mockDispute), uint128(block.number)); + // Check: revert? vm.expectRevert(abi.encodeWithSelector(IOracle.Oracle_InvalidDisputeId.selector, _getId(mockDispute))); @@ -712,6 +766,19 @@ contract Oracle_Unit_ResolveDispute is BaseTest { oracle.resolveDispute(mockRequest, mockResponse, mockDispute); } + /** + * @notice Revert if the dispute doesn't exist + */ + function test_resolveDispute_revertsIfInvalidDispute() public { + oracle.mock_setDisputeCreatedAt(_getId(mockDispute), 0); + + // Check: revert? + vm.expectRevert(abi.encodeWithSelector(IOracle.Oracle_InvalidDispute.selector)); + + // Test: try to resolve the dispute + oracle.resolveDispute(mockRequest, mockResponse, mockDispute); + } + /** * @notice Test the revert when the function is called with a dispute in unresolvable status */ @@ -723,8 +790,13 @@ contract Oracle_Unit_ResolveDispute is BaseTest { continue; } + bytes32 _responseId = _getId(mockResponse); + // Mock the dispute - oracle.mock_setDisputeOf(_getId(mockResponse), _disputeId); + oracle.mock_setDisputeOf(_responseId, _disputeId); + oracle.mock_setRequestCreatedAt(_getId(mockRequest), uint128(block.number)); + oracle.mock_setResponseCreatedAt(_responseId, uint128(block.number)); + oracle.mock_setDisputeCreatedAt(_disputeId, uint128(block.number)); oracle.mock_setDisputeStatus(_disputeId, IOracle.DisputeStatus(_status)); // Check: revert? @@ -753,6 +825,9 @@ contract Oracle_Unit_ResolveDispute is BaseTest { // Mock the dispute oracle.mock_setDisputeOf(_getId(mockResponse), _disputeId); oracle.mock_setDisputeStatus(_disputeId, IOracle.DisputeStatus.Escalated); + oracle.mock_setRequestCreatedAt(_requestId, uint128(block.number)); + oracle.mock_setResponseCreatedAt(_responseId, uint128(block.number)); + oracle.mock_setDisputeCreatedAt(_disputeId, uint128(block.number)); // Check: revert? vm.expectRevert(abi.encodeWithSelector(IOracle.Oracle_NoResolutionModule.selector, _disputeId)); @@ -832,7 +907,10 @@ contract Oracle_Unit_Finalize is BaseTest { bytes32 _requestId = _getId(mockRequest); mockResponse.requestId = _requestId; bytes32 _responseId = _getId(mockResponse); + oracle.mock_addResponseId(_requestId, _responseId); + oracle.mock_setRequestCreatedAt(_requestId, uint128(block.number)); + oracle.mock_setResponseCreatedAt(_responseId, uint128(block.number)); // Mock the finalize call on all modules bytes memory _calldata = abi.encodeCall(IModule.finalizeRequest, (mockRequest, mockResponse, _caller)); @@ -857,15 +935,45 @@ contract Oracle_Unit_Finalize is BaseTest { assertEq(oracle.finalizedAt(_requestId), block.number); } + /** + * @notice Revert if the request doesn't exist + */ + function test_finalize_revertsIfInvalidRequest() public { + oracle.mock_setRequestCreatedAt(_getId(mockRequest), 0); + vm.expectRevert(IOracle.Oracle_InvalidResponse.selector); + + vm.prank(requester); + oracle.finalize(mockRequest, mockResponse); + } + + /** + * @notice Revert if the response doesn't exist + */ + function test_finalize_revertsIfInvalidResponse() public { + bytes32 _requestId = _getId(mockRequest); + oracle.mock_setRequestCreatedAt(_requestId, uint128(block.number)); + oracle.mock_setResponseCreatedAt(_getId(mockResponse), 0); + + // Check: revert? + vm.expectRevert(IOracle.Oracle_InvalidResponse.selector); + + // Test: finalize the request + vm.prank(requester); + oracle.finalize(mockRequest, mockResponse); + } + /** * @notice Finalizing an already finalized request */ function test_finalize_withResponse_revertsWhenAlreadyFinalized() public { bytes32 _requestId = _getId(mockRequest); + bytes32 _responseId = _getId(mockResponse); // Test: finalize a finalized request oracle.mock_setFinalizedAt(_requestId, uint128(block.number)); - oracle.mock_addResponseId(_requestId, _getId(mockResponse)); + oracle.mock_addResponseId(_requestId, _responseId); + oracle.mock_setRequestCreatedAt(_requestId, uint128(block.number)); + oracle.mock_setResponseCreatedAt(_responseId, uint128(block.number)); vm.expectRevert(abi.encodeWithSelector(IOracle.Oracle_AlreadyFinalized.selector, _requestId)); vm.prank(requester); @@ -883,9 +991,11 @@ contract Oracle_Unit_Finalize is BaseTest { // Store the response oracle.mock_addResponseId(_requestId, _responseId); + oracle.mock_setRequestCreatedAt(_getId(mockRequest), uint128(block.number)); + oracle.mock_setResponseCreatedAt(_requestId, uint128(block.number)); // Test: finalize the request - vm.expectRevert(IOracle.Oracle_InvalidResponseBody.selector); + vm.expectRevert(ValidatorLib.ValidatorLib_InvalidResponseBody.selector); vm.prank(requester); oracle.finalize(mockRequest, mockResponse); } @@ -894,6 +1004,9 @@ contract Oracle_Unit_Finalize is BaseTest { * @notice Finalizing a request with an unrelated response */ function test_finalize_withResponse_revertsIfInvalidResponse() public { + oracle.mock_setRequestCreatedAt(_getId(mockRequest), uint128(block.number)); + oracle.mock_setResponseCreatedAt(_getId(mockResponse), uint128(block.number)); + // Test: finalize the request vm.expectRevert(IOracle.Oracle_InvalidFinalizedResponse.selector); vm.prank(requester); @@ -914,6 +1027,8 @@ contract Oracle_Unit_Finalize is BaseTest { // Submit a response to the request oracle.mock_addResponseId(_requestId, _responseId); + oracle.mock_setRequestCreatedAt(_requestId, uint128(block.number)); + oracle.mock_setResponseCreatedAt(_responseId, uint128(block.number)); oracle.mock_setDisputeOf(_responseId, _disputeId); oracle.mock_setDisputeStatus(_disputeId, IOracle.DisputeStatus.Won); @@ -936,6 +1051,7 @@ contract Oracle_Unit_Finalize is BaseTest { vm.assume(_caller != address(0)); bytes32 _requestId = _getId(mockRequest); + oracle.mock_setRequestCreatedAt(_requestId, uint128(block.number)); mockResponse.requestId = bytes32(0); // Create mock request and store it @@ -977,6 +1093,7 @@ contract Oracle_Unit_Finalize is BaseTest { vm.assume(_status <= uint256(type(IOracle.DisputeStatus).max)); bytes32 _requestId = _getId(mockRequest); + oracle.mock_setRequestCreatedAt(_requestId, uint128(block.number)); IOracle.DisputeStatus _disputeStatus = IOracle.DisputeStatus(_status); @@ -1014,6 +1131,7 @@ contract Oracle_Unit_Finalize is BaseTest { // Override the finalizedAt to make it be finalized oracle.mock_setFinalizedAt(_requestId, uint128(block.number)); + oracle.mock_setRequestCreatedAt(_getId(mockRequest), uint128(block.number)); // Test: finalize a finalized request vm.expectRevert(abi.encodeWithSelector(IOracle.Oracle_AlreadyFinalized.selector, _requestId)); @@ -1031,6 +1149,7 @@ contract Oracle_Unit_Finalize is BaseTest { // Submit a response to the request oracle.mock_addResponseId(_requestId, _responseId); + oracle.mock_setRequestCreatedAt(_getId(mockRequest), uint128(block.number)); // Check: reverts? vm.expectRevert(abi.encodeWithSelector(IOracle.Oracle_FinalizableResponseExists.selector, _responseId)); @@ -1050,6 +1169,9 @@ contract Oracle_Unit_EscalateDispute is BaseTest { oracle.mock_setDisputeOf(_getId(mockResponse), _disputeId); oracle.mock_setDisputeStatus(_disputeId, IOracle.DisputeStatus.Active); + oracle.mock_setRequestCreatedAt(_getId(mockRequest), uint128(block.number)); + oracle.mock_setResponseCreatedAt(_getId(mockResponse), uint128(block.number)); + oracle.mock_setDisputeCreatedAt(_disputeId, uint128(block.number)); // Mock and expect the dispute module call _mockAndExpect( @@ -1093,6 +1215,9 @@ contract Oracle_Unit_EscalateDispute is BaseTest { oracle.mock_setDisputeOf(_getId(mockResponse), _disputeId); oracle.mock_setDisputeStatus(_disputeId, IOracle.DisputeStatus.Active); + oracle.mock_setRequestCreatedAt(_requestId, uint128(block.number)); + oracle.mock_setResponseCreatedAt(_responseId, uint128(block.number)); + oracle.mock_setDisputeCreatedAt(_disputeId, uint128(block.number)); // Mock and expect the dispute module call _mockAndExpect( @@ -1112,11 +1237,28 @@ contract Oracle_Unit_EscalateDispute is BaseTest { assertEq(uint256(oracle.disputeStatus(_disputeId)), uint256(IOracle.DisputeStatus.Escalated)); } + /** + * /** + * @notice Revert if the dispute doesn't exist + */ + function test_escalateDispute_revertsIfInvalidDispute() public { + bytes32 _disputeId = _getId(mockDispute); + oracle.mock_setRequestCreatedAt(_getId(mockRequest), uint128(block.number)); + oracle.mock_setResponseCreatedAt(_getId(mockResponse), uint128(block.number)); + oracle.mock_setDisputeCreatedAt(_disputeId, 0); + vm.expectRevert(abi.encodeWithSelector(IOracle.Oracle_InvalidDispute.selector)); + + oracle.escalateDispute(mockRequest, mockResponse, mockDispute); + } + /** * @notice Revert if the provided dispute does not match the request or the response */ function test_escalateDispute_revertsIfDisputeNotValid() public { bytes32 _disputeId = _getId(mockDispute); + oracle.mock_setRequestCreatedAt(_getId(mockRequest), uint128(block.number)); + oracle.mock_setResponseCreatedAt(_getId(mockResponse), uint128(block.number)); + oracle.mock_setDisputeCreatedAt(_disputeId, uint128(block.number)); vm.expectRevert(abi.encodeWithSelector(IOracle.Oracle_InvalidDisputeId.selector, _disputeId)); // Test: escalate the dispute @@ -1125,6 +1267,9 @@ contract Oracle_Unit_EscalateDispute is BaseTest { function test_escalateDispute_revertsIfDisputeNotActive() public { bytes32 _disputeId = _getId(mockDispute); + oracle.mock_setRequestCreatedAt(_getId(mockRequest), uint128(block.number)); + oracle.mock_setResponseCreatedAt(_getId(mockResponse), uint128(block.number)); + oracle.mock_setDisputeCreatedAt(_disputeId, uint128(block.number)); oracle.mock_setDisputeOf(_getId(mockResponse), _disputeId); vm.expectRevert(abi.encodeWithSelector(IOracle.Oracle_CannotEscalate.selector, _disputeId)); diff --git a/solidity/test/unit/Validator.t.sol b/solidity/test/unit/Validator.t.sol index 182dbb55..d6d19d4a 100644 --- a/solidity/test/unit/Validator.t.sol +++ b/solidity/test/unit/Validator.t.sol @@ -12,8 +12,10 @@ import {IRequestModule} from '../../interfaces/modules/request/IRequestModule.so import {IResolutionModule} from '../../interfaces/modules/resolution/IResolutionModule.sol'; import {IResponseModule} from '../../interfaces/modules/response/IResponseModule.sol'; -import {Validator} from '../../contracts/Validator.sol'; -import {IValidator} from '../../interfaces/IValidator.sol'; +import {IValidator, Validator} from '../../contracts/Validator.sol'; + +import {ValidatorLib} from '../../lib/ValidatorLib.sol'; + import {Helpers} from '../utils/Helpers.sol'; /** @@ -65,15 +67,15 @@ contract MockValidator is Validator { } /** - * @title Oracle Unit tests + * @title Validator Unit tests */ contract BaseTest is Test, Helpers { - // The target contract - MockValidator public validator; - // Mock Oracle IOracle public oracle = IOracle(_mockContract('oracle')); + // The target contract + MockValidator public validator; + // Mock modules IRequestModule public requestModule = IRequestModule(_mockContract('requestModule')); IResponseModule public responseModule = IResponseModule(_mockContract('responseModule')); @@ -93,15 +95,6 @@ contract BaseTest is Test, Helpers { mockResponse.requestId = _getId(mockRequest); mockDispute.requestId = mockResponse.requestId; mockDispute.responseId = _getId(mockResponse); - - vm.mockCall( - address(oracle), - abi.encodeWithSelector(IOracle.responseCreatedAt.selector, _getId(mockResponse)), - abi.encode(1000) - ); - vm.mockCall( - address(oracle), abi.encodeWithSelector(IOracle.disputeCreatedAt.selector, _getId(mockDispute)), abi.encode(1000) - ); } } @@ -129,10 +122,10 @@ contract ValidatorValidateResponse is BaseTest { } function test_validateResponse_InvalidResponseBody() public { - IOracle.Response memory response = mockResponse; - response.requestId = bytes32('invalid'); - vm.expectRevert(IValidator.Validator_InvalidResponseBody.selector); - validator.validateResponse(mockRequest, response); + IOracle.Response memory _response = mockResponse; + _response.requestId = bytes32('invalid'); + vm.expectRevert(ValidatorLib.ValidatorLib_InvalidResponseBody.selector); + validator.validateResponse(mockRequest, _response); } function test_validateResponse_InvalidResponse() public { @@ -151,10 +144,10 @@ contract ValidatorValidateDisputeRequest is BaseTest { } function test_validateDispute_InvalidDisputeBody() public { - IOracle.Dispute memory dispute = mockDispute; - dispute.requestId = bytes32('invalid'); - vm.expectRevert(IValidator.Validator_InvalidDisputeBody.selector); - validator.validateDispute(mockRequest, dispute); + IOracle.Dispute memory _dispute = mockDispute; + _dispute.requestId = bytes32('invalid'); + vm.expectRevert(ValidatorLib.ValidatorLib_InvalidDisputeBody.selector); + validator.validateDispute(mockRequest, _dispute); } function test_validateDispute_InvalidDispute() public { @@ -173,10 +166,10 @@ contract ValidatorValidateDisputeResponse is BaseTest { } function test_validateDispute_InvalidDisputeBody() public { - IOracle.Dispute memory dispute = mockDispute; - dispute.responseId = bytes32('invalid'); - vm.expectRevert(IValidator.Validator_InvalidDisputeBody.selector); - validator.validateDispute(mockResponse, dispute); + IOracle.Dispute memory _dispute = mockDispute; + _dispute.responseId = bytes32('invalid'); + vm.expectRevert(ValidatorLib.ValidatorLib_InvalidDisputeBody.selector); + validator.validateDispute(mockResponse, _dispute); } function test_validateDispute_InvalidDispute() public { @@ -188,33 +181,33 @@ contract ValidatorValidateDisputeResponse is BaseTest { } } -contract ValidatorValidateResponseAndDispute is BaseTest { +contract Validator_ValidateResponseAndDispute is BaseTest { function test_validateResponseAndDispute() public { - (bytes32 responseId, bytes32 disputeId) = + (bytes32 _responseId, bytes32 _disputeId) = validator.validateResponseAndDispute(mockRequest, mockResponse, mockDispute); - assertEq(responseId, keccak256(abi.encode(mockResponse))); - assertEq(disputeId, keccak256(abi.encode(mockDispute))); + assertEq(_responseId, keccak256(abi.encode(mockResponse))); + assertEq(_disputeId, keccak256(abi.encode(mockDispute))); } function test_validateResponseAndDispute_InvalidResponseBody() public { - IOracle.Response memory response = mockResponse; - response.requestId = bytes32('invalid'); - vm.expectRevert(IValidator.Validator_InvalidResponseBody.selector); - validator.validateResponseAndDispute(mockRequest, response, mockDispute); + IOracle.Response memory _response = mockResponse; + _response.requestId = bytes32('invalid'); + vm.expectRevert(ValidatorLib.ValidatorLib_InvalidResponseBody.selector); + validator.validateResponseAndDispute(mockRequest, _response, mockDispute); } function test_validateResponseAndDispute_InvalidDisputeBody() public { - IOracle.Dispute memory dispute = mockDispute; - dispute.requestId = bytes32('invalid'); - vm.expectRevert(IValidator.Validator_InvalidDisputeBody.selector); - validator.validateResponseAndDispute(mockRequest, mockResponse, dispute); + IOracle.Dispute memory _dispute = mockDispute; + _dispute.requestId = bytes32('invalid'); + vm.expectRevert(ValidatorLib.ValidatorLib_InvalidDisputeBody.selector); + validator.validateResponseAndDispute(mockRequest, mockResponse, _dispute); } function test_validateResponseAndDispute_InvalidDisputeBodyResponseId() public { - IOracle.Dispute memory dispute = mockDispute; - dispute.responseId = bytes32('invalid'); - vm.expectRevert(IValidator.Validator_InvalidDisputeBody.selector); - validator.validateResponseAndDispute(mockRequest, mockResponse, dispute); + IOracle.Dispute memory _dispute = mockDispute; + _dispute.responseId = bytes32('invalid'); + vm.expectRevert(ValidatorLib.ValidatorLib_InvalidDisputeBody.selector); + validator.validateResponseAndDispute(mockRequest, mockResponse, _dispute); } function test_validateResponseAndDispute_InvalidDispute() public {