diff --git a/solidity/contracts/Oracle.sol b/solidity/contracts/Oracle.sol index 1eeeb6a..4d12171 100644 --- a/solidity/contracts/Oracle.sol +++ b/solidity/contracts/Oracle.sol @@ -102,12 +102,15 @@ contract Oracle is IOracle { /// @inheritdoc IOracle function listRequestIds(uint256 _startFrom, uint256 _batchSize) external view returns (bytes32[] memory _list) { - if (_batchSize > totalRequestCount) { + uint256 _totalRequestsCount = totalRequestCount; + + // If trying to collect non-existent ids only, return empty array + if (_startFrom > _totalRequestsCount) { return _list; } - if (_batchSize > totalRequestCount - _startFrom) { - _batchSize = totalRequestCount - _startFrom; + if (_batchSize > _totalRequestsCount - _startFrom) { + _batchSize = _totalRequestsCount - _startFrom; } _list = new bytes32[](_batchSize); @@ -378,7 +381,7 @@ contract Oracle is IOracle { DisputeStatus _disputeStatus = _disputes[_disputeId].status; if (_disputeStatus != DisputeStatus.None && _disputeStatus != DisputeStatus.Lost) { - revert Oracle_InvalidFinalizedResponse(_requestId); + revert Oracle_InvalidFinalizedResponse(_responseId); } unchecked { diff --git a/solidity/test/integration/EscalateDispute.t.sol b/solidity/test/integration/EscalateDispute.t.sol index 094f4ad..710eb1c 100644 --- a/solidity/test/integration/EscalateDispute.t.sol +++ b/solidity/test/integration/EscalateDispute.t.sol @@ -47,10 +47,6 @@ contract Integration_EscalateDispute is IntegrationBase { IOracle.Dispute memory _dispute = oracle.getDispute(_disputeId); assertTrue(_dispute.status == IOracle.DisputeStatus.Escalated); - /// TODO: The MockDisputeModule should now have the escalation status escalated - // IMockDisputeModule.BondEscalation memory _bondEscalation = _disputeModule.getEscalation(_requestId); - // assertTrue(_bondEscalation.status == IMockDisputeModule.BondEscalationStatus.Escalated); - /// Escalate dispute reverts if dispute is not active vm.expectRevert(abi.encodeWithSelector(IOracle.Oracle_CannotEscalate.selector, _disputeId)); oracle.escalateDispute(_disputeId); diff --git a/solidity/test/unit/Oracle.t.sol b/solidity/test/unit/Oracle.t.sol index 5d4a299..b57bc90 100644 --- a/solidity/test/unit/Oracle.t.sol +++ b/solidity/test/unit/Oracle.t.sol @@ -21,9 +21,10 @@ import {IModule} from '../../interfaces/IModule.sol'; * @dev Harness to deploy and test Oracle */ contract ForTest_Oracle is Oracle { - constructor() Oracle() {} - using EnumerableSet for EnumerableSet.Bytes32Set; + using EnumerableSet for EnumerableSet.AddressSet; + + constructor() Oracle() {} function forTest_setResponse(Response calldata _response) external returns (bytes32 _responseId) { _responseId = keccak256(abi.encodePacked(msg.sender, address(this), _response.requestId)); @@ -42,19 +43,45 @@ contract ForTest_Oracle is Oracle { function forTest_setResolutionModule(bytes32 _requestId, address _newResolutionModule) external { _requests[_requestId].resolutionModule = IResolutionModule(_newResolutionModule); } + + function forTest_responseNonce() external view returns (uint256 _nonce) { + _nonce = _responseNonce; + } + + function forTest_addParticipant(bytes32 _requestId, address _participant) external { + _participants[_requestId].add(_participant); + } + + function forTest_setFinalizedResponseId(bytes32 _requestId, bytes32 _finalizedResponseId) external { + _finalizedResponses[_requestId] = _finalizedResponseId; + } + + function forTest_setDisputeOf(bytes32 _responseId, bytes32 _disputeId) external { + disputeOf[_responseId] = _disputeId; + } + + function forTest_addResponseId(bytes32 _requestId, bytes32 _responseId) external { + _responseIds[_requestId].add(_responseId); + } + + function forTest_removeResponseId(bytes32 _requestId, bytes32 _responseId) external { + _responseIds[_requestId].remove(_responseId); + } } /** * @title Oracle Unit tests */ -contract Oracle_UnitTest is Test { +contract BaseTest is Test { using stdStorage for StdStorage; // The target contract ForTest_Oracle public oracle; // Mock addresses and contracts - address public sender = makeAddr('sender'); + address public requester = makeAddr('requester'); + address public proposer = makeAddr('proposer'); + address public disputer = makeAddr('disputer'); IRequestModule public requestModule = IRequestModule(makeAddr('requestModule')); IResponseModule public responseModule = IResponseModule(makeAddr('responseModule')); @@ -62,9 +89,12 @@ contract Oracle_UnitTest is Test { IResolutionModule public resolutionModule = IResolutionModule(makeAddr('resolutionModule')); IFinalityModule public finalityModule = IFinalityModule(makeAddr('finalityModule')); - // Create a new dummy dispute + // A dummy dispute IOracle.Dispute public mockDispute; + // A dummy response + IOracle.Response public mockResponse; + // 100% random sequence of bytes representing request, response, or dispute id bytes32 public mockId = bytes32('69'); @@ -80,7 +110,7 @@ contract Oracle_UnitTest is Test { /** * @notice Deploy the target and mock oracle+modules */ - function setUp() public { + function setUp() public virtual { oracle = new ForTest_Oracle(); vm.etch(address(requestModule), hex'69'); vm.etch(address(responseModule), hex'69'); @@ -90,14 +120,115 @@ contract Oracle_UnitTest is Test { mockDispute = IOracle.Dispute({ createdAt: block.timestamp, - disputer: sender, - proposer: sender, + disputer: disputer, + proposer: proposer, responseId: mockId, requestId: mockId, status: IOracle.DisputeStatus.Active }); + + mockResponse = IOracle.Response({ + createdAt: block.timestamp, + proposer: proposer, + requestId: mockId, + disputeId: mockId, + response: bytes('69') + }); } + /** + * @notice If no dispute and finality module used, set them to address 0 + */ + modifier setResolutionAndFinality(bool _useResolutionAndFinality) { + if (!_useResolutionAndFinality) { + disputeModule = IDisputeModule(address(0)); + finalityModule = IFinalityModule(address(0)); + } + _; + } + + /** + * @notice Combines mockCall and expectCall into one function + * + * @param _receiver The receiver of the calls + * @param _calldata The encoded selector and the parameters of the call + * @param _returned The encoded data that the call should return + */ + function _mockAndExpect(address _receiver, bytes memory _calldata, bytes memory _returned) internal { + vm.mockCall(_receiver, _calldata, _returned); + vm.expectCall(_receiver, _calldata); + } + + /** + * @notice Creates a mock request and stores it in the oracle + */ + function _mockRequest() internal returns (bytes32 _requestId, IOracle.NewRequest memory _request) { + (bytes32[] memory _ids, IOracle.NewRequest[] memory _requests) = _mockRequests(1); + _requestId = _ids[0]; + _request = _requests[0]; + } + + /** + * @notice create mock requests and store them in the oracle + * + * @dev each request has an incremental ipfsHash in order to easily test their content + * + * @param _howMany uint256 how many request to store + * + * @return _requestIds bytes32[] the request ids + */ + function _mockRequests(uint256 _howMany) + internal + returns (bytes32[] memory _requestIds, IOracle.NewRequest[] memory _requests) + { + _requestIds = new bytes32[](_howMany); + _requests = new IOracle.NewRequest[](_howMany); + + for (uint256 _i; _i < _howMany; _i++) { + IOracle.NewRequest memory _request = IOracle.NewRequest({ + requestModuleData: bytes('requestModuleData'), + responseModuleData: bytes('responseModuleData'), + disputeModuleData: bytes('disputeModuleData'), + resolutionModuleData: bytes('resolutionModuleData'), + finalityModuleData: bytes('finalityModuleData'), + ipfsHash: bytes32(_i), + requestModule: requestModule, + responseModule: responseModule, + disputeModule: disputeModule, + resolutionModule: resolutionModule, + finalityModule: finalityModule + }); + + address[] memory _modules = new address[](5); + _modules[0] = address(requestModule); + _modules[1] = address(responseModule); + _modules[2] = address(disputeModule); + _modules[3] = address(resolutionModule); + _modules[4] = address(finalityModule); + + bytes[] memory _moduleData = new bytes[](5); + _moduleData[0] = _request.requestModuleData; + _moduleData[1] = _request.responseModuleData; + _moduleData[2] = _request.disputeModuleData; + _moduleData[3] = _request.resolutionModuleData; + _moduleData[4] = _request.finalityModuleData; + + bytes32 _requestId = keccak256(abi.encodePacked(requester, address(oracle), oracle.totalRequestCount())); + + for (uint256 _x; _x < _modules.length; _x++) { + vm.mockCall(_modules[_x], abi.encodeCall(IModule.setupRequest, (_requestId, _moduleData[_x])), abi.encode()); + + vm.mockCall(_modules[_x], abi.encodeCall(IModule.requestData, (_requestId)), abi.encode(_moduleData[_x])); + } + + vm.prank(requester); + _requestIds[_i] = oracle.createRequest(_request); + _requests[_i] = _request; + } + } +} + +contract Unit_CreateRequest is BaseTest { /** * @notice Test the request creation, with correct arguments, and nonce increment. * @@ -110,13 +241,7 @@ contract Oracle_UnitTest is Test { bytes calldata _disputeData, bytes calldata _resolutionData, bytes calldata _finalityData - ) public { - // If no dispute and finality module used, set them to address 0 - if (!_useResolutionAndFinality) { - disputeModule = IDisputeModule(address(0)); - finalityModule = IFinalityModule(address(0)); - } - + ) public setResolutionAndFinality(_useResolutionAndFinality) { uint256 _initialNonce = oracle.totalRequestCount(); // Create the request @@ -135,76 +260,52 @@ contract Oracle_UnitTest is Test { }); // Compute the associated request id - bytes32 _theoreticalRequestId = keccak256(abi.encodePacked(sender, address(oracle), _initialNonce)); - - // If dispute and finality module != 0, mock and expect their calls - if (_useResolutionAndFinality) { - vm.mockCall( - address(disputeModule), - abi.encodeCall(IModule.setupRequest, (_theoreticalRequestId, _request.resolutionModuleData)), - abi.encode() - ); - vm.expectCall( - address(resolutionModule), - abi.encodeCall(IModule.setupRequest, (_theoreticalRequestId, _request.resolutionModuleData)) - ); - - vm.mockCall( - address(finalityModule), - abi.encodeCall(IModule.setupRequest, (_theoreticalRequestId, _request.finalityModuleData)), - abi.encode() - ); - vm.expectCall( - address(finalityModule), - abi.encodeCall(IModule.setupRequest, (_theoreticalRequestId, _request.finalityModuleData)) - ); - } + bytes32 _theoreticalRequestId = keccak256(abi.encodePacked(requester, address(oracle), _initialNonce)); - // mock and expect disputeModule call - vm.mockCall( + // Mock and expect setupRequest call on the required modules + _mockAndExpect( address(disputeModule), abi.encodeCall(IModule.setupRequest, (_theoreticalRequestId, _request.disputeModuleData)), abi.encode() ); - vm.expectCall( - address(disputeModule), abi.encodeCall(IModule.setupRequest, (_theoreticalRequestId, _request.disputeModuleData)) - ); - - // mock and expect requestModule and responseModule calls - vm.mockCall( + _mockAndExpect( address(requestModule), abi.encodeCall(IModule.setupRequest, (_theoreticalRequestId, _request.requestModuleData)), abi.encode() ); - vm.expectCall( - address(requestModule), abi.encodeCall(IModule.setupRequest, (_theoreticalRequestId, _request.requestModuleData)) - ); - - vm.mockCall( + _mockAndExpect( address(responseModule), abi.encodeCall(IModule.setupRequest, (_theoreticalRequestId, _request.responseModuleData)), abi.encode() ); - vm.expectCall( - address(responseModule), - abi.encodeCall(IModule.setupRequest, (_theoreticalRequestId, _request.responseModuleData)) - ); + + // If resolution and finality module != 0, mock and expect their calls + if (_useResolutionAndFinality) { + _mockAndExpect( + address(resolutionModule), + abi.encodeCall(IModule.setupRequest, (_theoreticalRequestId, _request.resolutionModuleData)), + abi.encode() + ); + _mockAndExpect( + address(finalityModule), + abi.encodeCall(IModule.setupRequest, (_theoreticalRequestId, _request.finalityModuleData)), + abi.encode() + ); + } // Check: emits RequestCreated event? vm.expectEmit(true, true, true, true); - emit RequestCreated(_theoreticalRequestId, sender); + emit RequestCreated(_theoreticalRequestId, requester); // Test: create the request - vm.prank(sender); + vm.prank(requester); bytes32 _requestId = oracle.createRequest(_request); - uint256 _newNonce = oracle.totalRequestCount(); - // Check: correct request id returned? assertEq(_requestId, _theoreticalRequestId); // Check: nonce incremented? - assertEq(_newNonce, _initialNonce + 1); + assertEq(oracle.totalRequestCount(), _initialNonce + 1); IOracle.Request memory _storedRequest = oracle.getRequest(_requestId); @@ -216,11 +317,13 @@ contract Oracle_UnitTest is Test { assertEq(address(_storedRequest.finalityModule), address(_request.finalityModule)); // Check: request values correctly stored - ones set by the oracle - assertEq(_storedRequest.requester, sender); // should be set + assertEq(_storedRequest.requester, requester); // should be set assertEq(_storedRequest.nonce, _initialNonce); assertEq(_storedRequest.createdAt, block.timestamp); // should be set } +} +contract Unit_CreateRequests is BaseTest { /** * @notice Test creation of requests in batch mode. */ @@ -230,15 +333,9 @@ contract Oracle_UnitTest is Test { bytes calldata _disputeData ) public { uint256 _initialNonce = oracle.totalRequestCount(); - uint256 _requestsAmount = 5; - - IOracle.NewRequest[] memory _requests = new IOracle.NewRequest[]( - _requestsAmount - ); - + IOracle.NewRequest[] memory _requests = new IOracle.NewRequest[](_requestsAmount); bytes32[] memory _precalculatedIds = new bytes32[](_requestsAmount); - bool _useResolutionAndFinality = _requestData.length % 2 == 0; // Generate requests batch @@ -262,70 +359,47 @@ contract Oracle_UnitTest is Test { finalityModule: finalityModule }); - bytes32 _theoreticalRequestId = keccak256(abi.encodePacked(sender, address(oracle), _initialNonce + _i)); + bytes32 _theoreticalRequestId = keccak256(abi.encodePacked(requester, address(oracle), _initialNonce + _i)); _requests[_i] = _request; _precalculatedIds[_i] = _theoreticalRequestId; - if (_useResolutionAndFinality) { - vm.mockCall( - address(disputeModule), - abi.encodeCall(IModule.setupRequest, (_theoreticalRequestId, _request.resolutionModuleData)), - abi.encode() - ); - vm.expectCall( - address(resolutionModule), - abi.encodeCall(IModule.setupRequest, (_theoreticalRequestId, _request.resolutionModuleData)) - ); - - vm.mockCall( - address(finalityModule), - abi.encodeCall(IModule.setupRequest, (_theoreticalRequestId, _request.finalityModuleData)), - abi.encode() - ); - vm.expectCall( - address(finalityModule), - abi.encodeCall(IModule.setupRequest, (_theoreticalRequestId, _request.finalityModuleData)) - ); - } - - // mock and expect disputeModule call - vm.mockCall( + // Mock and expect setupRequest call on the required modules + _mockAndExpect( address(disputeModule), abi.encodeCall(IModule.setupRequest, (_theoreticalRequestId, _request.disputeModuleData)), abi.encode() ); - vm.expectCall( - address(disputeModule), - abi.encodeCall(IModule.setupRequest, (_theoreticalRequestId, _request.disputeModuleData)) - ); - - // mock and expect requestModule and responseModule calls - vm.mockCall( + _mockAndExpect( address(requestModule), abi.encodeCall(IModule.setupRequest, (_theoreticalRequestId, _request.requestModuleData)), abi.encode() ); - vm.expectCall( - address(requestModule), - abi.encodeCall(IModule.setupRequest, (_theoreticalRequestId, _request.requestModuleData)) - ); - - vm.mockCall( + _mockAndExpect( address(responseModule), abi.encodeCall(IModule.setupRequest, (_theoreticalRequestId, _request.responseModuleData)), abi.encode() ); - vm.expectCall( - address(responseModule), - abi.encodeCall(IModule.setupRequest, (_theoreticalRequestId, _request.responseModuleData)) - ); + + // If resolution and finality module != 0, mock and expect their calls + if (_useResolutionAndFinality) { + _mockAndExpect( + address(resolutionModule), + abi.encodeCall(IModule.setupRequest, (_theoreticalRequestId, _request.resolutionModuleData)), + abi.encode() + ); + _mockAndExpect( + address(finalityModule), + abi.encodeCall(IModule.setupRequest, (_theoreticalRequestId, _request.finalityModuleData)), + abi.encode() + ); + } // Check: emits RequestCreated event? vm.expectEmit(true, true, true, true); - emit RequestCreated(_theoreticalRequestId, sender); + emit RequestCreated(_theoreticalRequestId, requester); } - vm.prank(sender); + vm.prank(requester); bytes32[] memory _requestsIds = oracle.createRequests(_requests); for (uint256 _i = 0; _i < _requestsIds.length; _i++) { @@ -341,7 +415,7 @@ contract Oracle_UnitTest is Test { assertEq(address(_storedRequest.finalityModule), address(_requests[_i].finalityModule)); // Check: request values correctly stored - ones set by the oracle - assertEq(_storedRequest.requester, sender); // should be set + assertEq(_storedRequest.requester, requester); // should be set assertEq(_storedRequest.nonce, _initialNonce + _i); assertEq(_storedRequest.createdAt, block.timestamp); // should be set } @@ -349,7 +423,9 @@ contract Oracle_UnitTest is Test { uint256 _newNonce = oracle.totalRequestCount(); assertEq(_newNonce, _initialNonce + _requestsAmount); } +} +contract Unit_ListRequests is BaseTest { /** * @notice Test list requests, fuzz start and batch size */ @@ -358,7 +434,7 @@ contract Oracle_UnitTest is Test { _howMany = bound(_howMany, 0, 10); // Store mock requests and mock the associated requestData calls - (bytes32[] memory _dummyRequestIds, IOracle.NewRequest[] memory _dummyRequests) = _storeDummyRequests(_howMany); + (bytes32[] memory _mockRequestIds, IOracle.NewRequest[] memory _mockRequests) = _mockRequests(_howMany); // Test: fetching the requests IOracle.FullRequest[] memory _requests = oracle.listRequests(0, _howMany); @@ -369,19 +445,19 @@ contract Oracle_UnitTest is Test { // Check: correct requests returned (dummy are incremented)? for (uint256 _i; _i < _howMany; _i++) { // Params copied: - assertEq(_requests[_i].ipfsHash, _dummyRequests[_i].ipfsHash); - assertEq(address(_requests[_i].requestModule), address(_dummyRequests[_i].requestModule)); - assertEq(address(_requests[_i].responseModule), address(_dummyRequests[_i].responseModule)); - assertEq(address(_requests[_i].disputeModule), address(_dummyRequests[_i].disputeModule)); - assertEq(address(_requests[_i].resolutionModule), address(_dummyRequests[_i].resolutionModule)); - assertEq(address(_requests[_i].finalityModule), address(_dummyRequests[_i].finalityModule)); + assertEq(_requests[_i].ipfsHash, _mockRequests[_i].ipfsHash); + assertEq(address(_requests[_i].requestModule), address(_mockRequests[_i].requestModule)); + assertEq(address(_requests[_i].responseModule), address(_mockRequests[_i].responseModule)); + assertEq(address(_requests[_i].disputeModule), address(_mockRequests[_i].disputeModule)); + assertEq(address(_requests[_i].resolutionModule), address(_mockRequests[_i].resolutionModule)); + assertEq(address(_requests[_i].finalityModule), address(_mockRequests[_i].finalityModule)); // Params created in createRequest: assertEq(_requests[_i].nonce, _i); - assertEq(_requests[_i].requester, sender); + assertEq(_requests[_i].requester, requester); assertEq(_requests[_i].createdAt, block.timestamp); - assertEq(_requests[_i].requestId, _dummyRequestIds[_i]); + assertEq(_requests[_i].requestId, _mockRequestIds[_i]); // Params gathered from external modules: assertEq(_requests[_i].requestModuleData, bytes('requestModuleData')); @@ -402,7 +478,7 @@ contract Oracle_UnitTest is Test { _howMany = bound(_howMany, 1, 10); // Store mock requests - _storeDummyRequests(_howMany); + _mockRequests(_howMany); // Test: fetching 1 extra request IOracle.FullRequest[] memory _requests = oracle.listRequests(0, _howMany + 1); @@ -415,6 +491,10 @@ contract Oracle_UnitTest is Test { assertEq(_requests[_i].ipfsHash, bytes32(_i)); assertEq(_requests[_i].nonce, _i); } + + // Test: starting from an index outside of the range + _requests = oracle.listRequests(_howMany + 1, _howMany); + assertEq(_requests.length, 0); } /** @@ -427,15 +507,78 @@ contract Oracle_UnitTest is Test { // Check; 0 returned? assertEq(_requests.length, 0); } +} +contract Unit_ListRequestIds is BaseTest { + /** + * @notice Test list requests ids, fuzz start and batch size + */ + function test_listRequestIds(uint256 _howMany) public { + // 0 to 10 request to list, fuzzed + _howMany = bound(_howMany, 0, 10); + + // Store mock requests and mock the associated requestData calls + (bytes32[] memory _mockRequestIds,) = _mockRequests(_howMany); + + // Test: fetching the requests + bytes32[] memory _requestsIds = oracle.listRequestIds(0, _howMany); + + // Check: enough request returned? + assertEq(_requestsIds.length, _howMany); + + // Check: correct requests returned (dummy are incremented)? + for (uint256 _i; _i < _howMany; _i++) { + assertEq(_requestsIds[_i], _mockRequestIds[_i]); + } + } + + /** + * @notice Test the request listing if asking for more request than it exists + * + * @dev This is testing _startFrom + _batchSize > _nonce scenario + */ + function test_listRequestIdsTooManyRequested(uint256 _howMany) public { + // 1 to 10 request to list, fuzzed + _howMany = bound(_howMany, 1, 10); + + // Store mock requests + (bytes32[] memory _mockRequestIds,) = _mockRequests(_howMany); + + // Test: fetching 1 extra request + bytes32[] memory _requestsIds = oracle.listRequestIds(0, _howMany + 1); + + // Check: correct number of request returned? + assertEq(_requestsIds.length, _howMany); + + // Check: correct data? + for (uint256 _i; _i < _howMany; _i++) { + assertEq(_requestsIds[_i], _mockRequestIds[_i]); + } + + // Test: starting from an index outside of the range + _requestsIds = oracle.listRequestIds(_howMany + 1, _howMany); + assertEq(_requestsIds.length, 0); + } + + /** + * @notice Test the request listing if there are no requests encoded + */ + function test_listRequestIdsZeroToReturn(uint256 _howMany) public { + // Test: fetch any number of requests + bytes32[] memory _requestsIds = oracle.listRequestIds(0, _howMany); + + // Check; 0 returned? + assertEq(_requestsIds.length, 0); + } +} + +contract Unit_GetRequestByNonce is BaseTest { function test_getRequestByNonce() public { uint256 _totalRequestCount = oracle.totalRequestCount(); // Store mock requests and mock the associated requestData calls - (, IOracle.NewRequest[] memory _dummyRequests) = _storeDummyRequests(1); - + (, IOracle.NewRequest memory _expectedRequest) = _mockRequest(); IOracle.Request memory _request = oracle.getRequestByNonce(_totalRequestCount); - IOracle.NewRequest memory _expectedRequest = _dummyRequests[_totalRequestCount]; assertEq(_request.ipfsHash, _expectedRequest.ipfsHash); assertEq(address(_request.requestModule), address(_expectedRequest.requestModule)); @@ -446,70 +589,81 @@ contract Oracle_UnitTest is Test { // Params created in createRequest: assertEq(_request.nonce, _totalRequestCount); - assertEq(_request.requester, sender); + assertEq(_request.requester, requester); assertEq(_request.createdAt, block.timestamp); } +} +contract Unit_GetRequestId is BaseTest { function test_getRequestId() public { uint256 _totalRequestCount = oracle.totalRequestCount(); // Store mock requests and mock the associated requestData calls - (bytes32[] memory _dummyRequestIds,) = _storeDummyRequests(1); - + (bytes32 _expectedRequestId,) = _mockRequest(); bytes32 _requestId = oracle.getRequestId(_totalRequestCount); - bytes32 _expectedRequestId = _dummyRequestIds[0]; assertEq(_requestId, _expectedRequestId); } +} +contract Unit_ProposeResponse is BaseTest { /** * @notice Test propose response: check _responses, _responseIds and _responseId */ function test_proposeResponse(bytes calldata _responseData) public { // Create mock request and store it - (bytes32[] memory _dummyRequestIds,) = _storeDummyRequests(1); - bytes32 _requestId = _dummyRequestIds[0]; + (bytes32 _requestId,) = _mockRequest(); - // Get the current response nonce (7th slot) - uint256 _responseNonce = uint256(vm.load(address(oracle), bytes32(uint256(0x7)))); + // Get the current response nonce + uint256 _responseNonce = oracle.forTest_responseNonce(); // Compute the response ID - bytes32 _responseId = keccak256(abi.encodePacked(sender, address(oracle), _requestId, _responseNonce)); + bytes32 _responseId = keccak256(abi.encodePacked(proposer, address(oracle), _requestId, _responseNonce)); // Create mock response - IOracle.Response memory _response = IOracle.Response({ - createdAt: block.timestamp, - proposer: sender, - requestId: _requestId, - disputeId: bytes32('69'), - response: _responseData - }); + mockResponse.requestId = _requestId; + mockResponse.response = _responseData; + + // Setting incorrect proposer to simulate tampering with the response + mockResponse.proposer = address(this); - // Mock&expect the responseModule propose call: - vm.mockCall( + // Mock and expect the responseModule propose call: + _mockAndExpect( address(responseModule), - abi.encodeCall(IResponseModule.propose, (_requestId, sender, _responseData, sender)), - abi.encode(_response) + abi.encodeCall(IResponseModule.propose, (_requestId, proposer, _responseData, proposer)), + abi.encode(mockResponse) ); - vm.expectCall( - address(responseModule), abi.encodeCall(IResponseModule.propose, (_requestId, sender, _responseData, sender)) + + // Test: propose the response + vm.expectRevert(abi.encodeWithSelector(IOracle.Oracle_CannotTamperParticipant.selector)); + vm.prank(proposer); + oracle.proposeResponse(_requestId, _responseData); + + // Change the proposer address + mockResponse.proposer = proposer; + + // Mock and expect the responseModule propose call: + _mockAndExpect( + address(responseModule), + abi.encodeCall(IResponseModule.propose, (_requestId, proposer, _responseData, proposer)), + abi.encode(mockResponse) ); // Check: emits ResponseProposed event? vm.expectEmit(true, true, true, true); - emit ResponseProposed(_requestId, sender, _responseId); + emit ResponseProposed(_requestId, proposer, _responseId); // Test: propose the response - vm.prank(sender); + vm.prank(proposer); bytes32 _actualResponseId = oracle.proposeResponse(_requestId, _responseData); // Check: emits ResponseProposed event? vm.expectEmit(true, true, true, true); emit ResponseProposed( - _requestId, sender, keccak256(abi.encodePacked(sender, address(oracle), _requestId, _responseNonce + 1)) + _requestId, proposer, keccak256(abi.encodePacked(proposer, address(oracle), _requestId, _responseNonce + 1)) ); - vm.prank(sender); + vm.prank(proposer); bytes32 _secondResponseId = oracle.proposeResponse(_requestId, _responseData); // Check: correct response id returned? @@ -521,11 +675,11 @@ contract Oracle_UnitTest is Test { IOracle.Response memory _storedResponse = oracle.getResponse(_responseId); // Check: correct response stored? - assertEq(_storedResponse.createdAt, _response.createdAt); - assertEq(_storedResponse.proposer, _response.proposer); - assertEq(_storedResponse.requestId, _response.requestId); - assertEq(_storedResponse.disputeId, _response.disputeId); - assertEq(_storedResponse.response, _response.response); + assertEq(_storedResponse.createdAt, mockResponse.createdAt); + assertEq(_storedResponse.proposer, mockResponse.proposer); + assertEq(_storedResponse.requestId, mockResponse.requestId); + assertEq(_storedResponse.disputeId, mockResponse.disputeId); + assertEq(_storedResponse.response, mockResponse.response); bytes32[] memory _responseIds = oracle.getResponseIds(_requestId); @@ -539,8 +693,7 @@ contract Oracle_UnitTest is Test { vm.assume(_finalizedAt > 0); // Create mock request - (bytes32[] memory _dummyRequestIds,) = _storeDummyRequests(1); - bytes32 _requestId = _dummyRequestIds[0]; + (bytes32 _requestId,) = _mockRequest(); IOracle.Request memory _request = oracle.getRequest(_requestId); // Override the finalizedAt to make it be finalized @@ -551,7 +704,9 @@ contract Oracle_UnitTest is Test { vm.expectRevert(abi.encodeWithSelector(IOracle.Oracle_AlreadyFinalized.selector, (_requestId))); oracle.proposeResponse(_requestId, _responseData); } +} +contract Unit_ProposeResponseWithProposer is BaseTest { /** * @notice Test dispute module proposes a response as somebody else: check _responses, _responseIds and _responseId */ @@ -559,38 +714,29 @@ contract Oracle_UnitTest is Test { vm.assume(_proposer != address(0)); // Create mock request and store it - (bytes32[] memory _dummyRequestIds,) = _storeDummyRequests(1); - bytes32 _requestId = _dummyRequestIds[0]; + (bytes32 _requestId,) = _mockRequest(); - // Get the current response nonce (7th slot) - uint256 _responseNonce = uint256(vm.load(address(oracle), bytes32(uint256(0x7)))); + // Get the current response nonce + uint256 _responseNonce = oracle.forTest_responseNonce(); // Compute the response ID bytes32 _responseId = keccak256(abi.encodePacked(_proposer, address(oracle), _requestId, _responseNonce)); // Create mock response - IOracle.Response memory _response = IOracle.Response({ - createdAt: block.timestamp, - proposer: _proposer, - requestId: _requestId, - disputeId: bytes32('69'), - response: _responseData - }); + mockResponse.proposer = _proposer; + mockResponse.requestId = _requestId; + mockResponse.response = _responseData; // Test: revert if called by a random dude (not dispute module) - vm.expectRevert(abi.encodeWithSelector(IOracle.Oracle_NotDisputeModule.selector, sender)); - vm.prank(sender); + vm.expectRevert(abi.encodeWithSelector(IOracle.Oracle_NotDisputeModule.selector, _proposer)); + vm.prank(_proposer); oracle.proposeResponse(_proposer, _requestId, _responseData); - // Mock&expect the responseModule propose call: - vm.mockCall( + // Mock and expect the responseModule propose call: + _mockAndExpect( address(responseModule), abi.encodeCall(IResponseModule.propose, (_requestId, _proposer, _responseData, address(disputeModule))), - abi.encode(_response) - ); - vm.expectCall( - address(responseModule), - abi.encodeCall(IResponseModule.propose, (_requestId, _proposer, _responseData, address(disputeModule))) + abi.encode(mockResponse) ); // Check: emits ResponseProposed event? @@ -619,11 +765,11 @@ contract Oracle_UnitTest is Test { IOracle.Response memory _storedResponse = oracle.getResponse(_responseId); // Check: correct response stored? - assertEq(_storedResponse.createdAt, _response.createdAt); - assertEq(_storedResponse.proposer, _response.proposer); - assertEq(_storedResponse.requestId, _response.requestId); - assertEq(_storedResponse.disputeId, _response.disputeId); - assertEq(_storedResponse.response, _response.response); + assertEq(_storedResponse.createdAt, mockResponse.createdAt); + assertEq(_storedResponse.proposer, mockResponse.proposer); + assertEq(_storedResponse.requestId, mockResponse.requestId); + assertEq(_storedResponse.disputeId, mockResponse.disputeId); + assertEq(_storedResponse.response, mockResponse.response); bytes32[] memory _responseIds = oracle.getResponseIds(_requestId); @@ -632,50 +778,42 @@ contract Oracle_UnitTest is Test { assertEq(_responseIds[0], _responseId); assertEq(_responseIds[1], _secondResponseId); } +} +contract Unit_DeleteResponse is BaseTest { /** * @notice Test response deletion */ function test_deleteResponse() public { // Create mock request and store it - (bytes32[] memory _dummyRequestIds,) = _storeDummyRequests(1); - bytes32 _requestId = _dummyRequestIds[0]; + (bytes32 _requestId,) = _mockRequest(); // Create mock response - IOracle.Response memory _response = IOracle.Response({ - createdAt: block.timestamp, - proposer: sender, - requestId: _requestId, - disputeId: bytes32('69'), - response: bytes('response') - }); + mockResponse.requestId = _requestId; - vm.mockCall( + _mockAndExpect( address(responseModule), - abi.encodeCall(IResponseModule.propose, (_requestId, sender, bytes('response'), sender)), - abi.encode(_response) + abi.encodeCall(IResponseModule.propose, (_requestId, proposer, bytes('response'), proposer)), + abi.encode(mockResponse) ); - vm.prank(sender); + vm.prank(proposer); bytes32 _responseId = oracle.proposeResponse(_requestId, bytes('response')); - vm.mockCall( + _mockAndExpect( address(responseModule), - abi.encodeCall(IResponseModule.deleteResponse, (_requestId, _responseId, sender)), + abi.encodeCall(IResponseModule.deleteResponse, (_requestId, _responseId, proposer)), abi.encode() ); - vm.expectCall( - address(responseModule), abi.encodeCall(IResponseModule.deleteResponse, (_requestId, _responseId, sender)) - ); bytes32[] memory _responsesIds = oracle.getResponseIds(_requestId); assertEq(_responsesIds.length, 1); // Check: is event emitted? vm.expectEmit(true, true, true, true); - emit ResponseDeleted(_requestId, sender, _responseId); + emit ResponseDeleted(_requestId, proposer, _responseId); - vm.prank(sender); + vm.prank(proposer); oracle.deleteResponse(_responseId); IOracle.Response memory _deletedResponse = oracle.getResponse(_responseId); @@ -693,112 +831,123 @@ contract Oracle_UnitTest is Test { function test_deleteResponseRevertsIfThereIsDispute() public { // Create mock request and store it - (bytes32[] memory _dummyRequestIds,) = _storeDummyRequests(1); - bytes32 _requestId = _dummyRequestIds[0]; - - address _proposer = makeAddr('proposer'); + (bytes32 _requestId,) = _mockRequest(); // Create mock response and store it - IOracle.Response memory _response = IOracle.Response({ - createdAt: block.timestamp, - proposer: _proposer, - requestId: _requestId, - disputeId: bytes32('69'), - response: bytes('69') - }); + mockResponse.requestId = _requestId; - bytes32 _responseId = oracle.forTest_setResponse(_response); + bytes32 _responseId = oracle.forTest_setResponse(mockResponse); - // Mock&expect the disputeModule disputeResponse call - vm.mockCall( + // Mock and expect the disputeModule disputeResponse call + _mockAndExpect( address(disputeModule), - abi.encodeCall(IDisputeModule.disputeResponse, (_requestId, _responseId, sender, _proposer)), + abi.encodeCall(IDisputeModule.disputeResponse, (_requestId, _responseId, disputer, proposer)), abi.encode(mockDispute) ); // Test: dispute the response - vm.prank(sender); + vm.prank(disputer); oracle.disputeResponse(_requestId, _responseId); vm.expectRevert(abi.encodeWithSelector(IOracle.Oracle_CannotDeleteWhileDisputing.selector, _responseId)); - vm.prank(sender); + vm.prank(proposer); oracle.deleteResponse(_responseId); } function test_deleteResponseRevertsIfInvalidSender() public { // Create mock request and store it - (bytes32[] memory _dummyRequestIds,) = _storeDummyRequests(1); - bytes32 _requestId = _dummyRequestIds[0]; - - address _proposer = makeAddr('proposer'); + (bytes32 _requestId,) = _mockRequest(); // Create mock response and store it - IOracle.Response memory _response = IOracle.Response({ - createdAt: block.timestamp, - proposer: _proposer, - requestId: _requestId, - disputeId: bytes32('69'), - response: bytes('69') - }); - - bytes32 _responseId = oracle.forTest_setResponse(_response); + mockResponse.requestId = _requestId; + bytes32 _responseId = oracle.forTest_setResponse(mockResponse); - // Mock&expect the disputeModule disputeResponse call - vm.mockCall( - address(disputeModule), - abi.encodeCall(IDisputeModule.disputeResponse, (_requestId, _responseId, sender, _proposer)), - abi.encode(mockDispute) - ); - - vm.expectRevert( - abi.encodeWithSelector(IOracle.Oracle_CannotDeleteInvalidProposer.selector, address(this), _responseId) - ); + vm.expectRevert(abi.encodeWithSelector(IOracle.Oracle_CannotDeleteInvalidProposer.selector, requester, _responseId)); + vm.prank(requester); oracle.deleteResponse(_responseId); } +} + +contract Unit_DisputeResponse is BaseTest { + using stdStorage for StdStorage; /** * @notice Test dispute response: check _responses, _responseIds and _responseId */ function test_disputeResponse() public { // Create mock request and store it - (bytes32[] memory _dummyRequestIds,) = _storeDummyRequests(1); - bytes32 _requestId = _dummyRequestIds[0]; - - address _proposer = makeAddr('proposer'); + (bytes32 _requestId,) = _mockRequest(); // Create mock response and store it - IOracle.Response memory _response = IOracle.Response({ - createdAt: block.timestamp, - proposer: _proposer, - requestId: _requestId, - disputeId: bytes32('69'), - response: bytes('69') - }); + mockResponse.requestId = _requestId; + bytes32 _responseId = oracle.forTest_setResponse(mockResponse); + bytes32 _disputeId = keccak256(abi.encodePacked(disputer, _requestId, _responseId)); - bytes32 _responseId = oracle.forTest_setResponse(_response); + // Setting incorrect disputer to test tampering with the dispute + mockDispute.disputer = address(this); - // Compute the dispute ID - bytes32 _disputeId = keccak256(abi.encodePacked(sender, _requestId, _responseId)); - - // Mock&expect the disputeModule disputeResponse call - vm.mockCall( + _mockAndExpect( address(disputeModule), - abi.encodeCall(IDisputeModule.disputeResponse, (_requestId, _responseId, sender, _proposer)), + abi.encodeCall(IDisputeModule.disputeResponse, (_requestId, _responseId, disputer, proposer)), abi.encode(mockDispute) ); - vm.expectCall( + + vm.expectRevert(abi.encodeWithSelector(IOracle.Oracle_CannotTamperParticipant.selector)); + vm.prank(disputer); + oracle.disputeResponse(_requestId, _responseId); + + // Set a correct disputer and any status but Active + mockDispute.disputer = disputer; + + for (uint256 _i; _i < uint256(type(IOracle.DisputeStatus).max); _i++) { + if (_i == uint256(IOracle.DisputeStatus.Active)) { + continue; + } + + // Set the new status + mockDispute.status = IOracle.DisputeStatus(_i); + + // Reset the request's finalization state + IOracle.Request memory _request = oracle.getRequest(_requestId); + _request.finalizedAt = 0; + oracle.forTest_setRequest(_requestId, _request); + + // Mock and expect the disputeModule disputeResponse call + _mockAndExpect( + address(disputeModule), + abi.encodeCall(IDisputeModule.disputeResponse, (_requestId, _responseId, disputer, proposer)), + abi.encode(mockDispute) + ); + + _mockAndExpect( + address(disputeModule), + abi.encodeCall(IDisputeModule.onDisputeStatusChange, (_disputeId, mockDispute)), + abi.encode() + ); + + vm.prank(disputer); + oracle.disputeResponse(_requestId, _responseId); + + // Reset the dispute of the response + oracle.forTest_setDisputeOf(_responseId, bytes32(0)); + } + + mockDispute.status = IOracle.DisputeStatus.Active; + // Mock and expect the disputeModule disputeResponse call + _mockAndExpect( address(disputeModule), - abi.encodeCall(IDisputeModule.disputeResponse, (_requestId, _responseId, sender, _proposer)) + abi.encodeCall(IDisputeModule.disputeResponse, (_requestId, _responseId, disputer, proposer)), + abi.encode(mockDispute) ); // Check: emits ResponseDisputed event? vm.expectEmit(true, true, true, true); - emit ResponseDisputed(sender, _responseId, _disputeId); + emit ResponseDisputed(disputer, _responseId, _disputeId); // Test: dispute the response - vm.prank(sender); + vm.prank(disputer); bytes32 _actualDisputeId = oracle.disputeResponse(_requestId, _responseId); // Check: correct dispute id returned? @@ -814,7 +963,6 @@ contract Oracle_UnitTest is Test { assertEq(_storedDispute.responseId, mockDispute.responseId); assertEq(_storedDispute.requestId, mockDispute.requestId); assertEq(uint256(_storedDispute.status), uint256(mockDispute.status)); - assertEq(_storedResponse.disputeId, _disputeId); } @@ -831,22 +979,20 @@ contract Oracle_UnitTest is Test { vm.expectRevert(abi.encodeWithSelector(IOracle.Oracle_ResponseAlreadyDisputed.selector, _responseId)); // Test: try to dispute the response again - vm.prank(sender); - oracle.disputeResponse(bytes32('69'), _responseId); + vm.prank(disputer); + oracle.disputeResponse(mockId, _responseId); } +} +contract Unit_UpdateDisputeStatus is BaseTest { /** * @notice update dispute status expect call to disputeModule * * @dev This is testing every combination of previous and new status (4x4) */ - function test_updateDisputeStatus() public { + function test_updateDisputeStatus(bytes32 _disputeId) public { // Create mock request and store it - (bytes32[] memory _dummyRequestIds,) = _storeDummyRequests(1); - bytes32 _requestId = _dummyRequestIds[0]; - - // Create a dummy dispute - bytes32 _disputeId = bytes32('69'); + (bytes32 _requestId,) = _mockRequest(); // Try every initial status for (uint256 _previousStatus; _previousStatus < uint256(type(IOracle.DisputeStatus).max); _previousStatus++) { @@ -862,15 +1008,12 @@ contract Oracle_UnitTest is Test { // The mocked call is done with the new status mockDispute.status = IOracle.DisputeStatus(_newStatus); - // Mock&expect the disputeModule onDisputeStatusChange call - vm.mockCall( + // Mock and expect the disputeModule onDisputeStatusChange call + _mockAndExpect( address(disputeModule), abi.encodeCall(IDisputeModule.onDisputeStatusChange, (_disputeId, mockDispute)), abi.encode() ); - vm.expectCall( - address(disputeModule), abi.encodeCall(IDisputeModule.onDisputeStatusChange, (_disputeId, mockDispute)) - ); // Check: emits DisputeStatusUpdated event? vm.expectEmit(true, true, true, true); @@ -887,22 +1030,41 @@ contract Oracle_UnitTest is Test { } } + /** + * @notice update dispute status revert if sender not resolution module + */ + function test_updateDisputeStatusRevertIfCallerNotResolutionModule(uint256 _newStatus, bytes32 _disputeId) public { + // 0 to 3 status, fuzzed + _newStatus = bound(_newStatus, 0, 3); + + // Store mock request + _mockRequest(); + + // Check: revert? + vm.expectRevert(abi.encodeWithSelector(IOracle.Oracle_NotResolutionModule.selector, proposer)); + + // Test: try to update the status from an EOA + vm.prank(proposer); + oracle.updateDisputeStatus(_disputeId, IOracle.DisputeStatus(_newStatus)); + } +} + +contract Unit_ResolveDispute is BaseTest { /** * @notice resolveDispute is expected to call resolution module */ - function test_resolveDispute() public { + function test_resolveDispute(bytes32 _disputeId) public { // Create mock request and store it - (bytes32[] memory _dummyRequestIds,) = _storeDummyRequests(1); - bytes32 _requestId = _dummyRequestIds[0]; + (bytes32 _requestId,) = _mockRequest(); // Create a dummy dispute - bytes32 _disputeId = bytes32('69'); mockDispute.requestId = _requestId; oracle.forTest_setDispute(_disputeId, mockDispute); // Mock and expect the resolution module call - vm.mockCall(address(resolutionModule), abi.encodeCall(IResolutionModule.resolveDispute, (_disputeId)), abi.encode()); - vm.expectCall(address(resolutionModule), abi.encodeCall(IResolutionModule.resolveDispute, (_disputeId))); + _mockAndExpect( + address(resolutionModule), abi.encodeCall(IResolutionModule.resolveDispute, (_disputeId)), abi.encode() + ); // Check: emits DisputeResolved event? vm.expectEmit(true, true, true, true); @@ -926,10 +1088,7 @@ contract Oracle_UnitTest is Test { /** * @notice Test the revert when the function is called but no resolution module was configured */ - function test_resolveDisputeRevertsIfWrongDisputeStatus() public { - // Create a dummy dispute - bytes32 _disputeId = bytes32('69'); - + function test_resolveDisputeRevertsIfWrongDisputeStatus(bytes32 _disputeId) public { for (uint256 _status; _status < uint256(type(IOracle.DisputeStatus).max); _status++) { if ( IOracle.DisputeStatus(_status) == IOracle.DisputeStatus.Active @@ -953,14 +1112,11 @@ contract Oracle_UnitTest is Test { /** * @notice Test the revert when the function is called with a non-active and non-escalated dispute */ - function test_resolveDisputeRevertsIfNoResolutionModule() public { - // Create a dummy dispute - bytes32 _disputeId = bytes32('69'); + function test_resolveDisputeRevertsIfNoResolutionModule(bytes32 _disputeId) public { oracle.forTest_setDispute(_disputeId, mockDispute); // Change the request of this dispute so that it does not have a resolution module - (bytes32[] memory _dummyRequestIds,) = _storeDummyRequests(1); - bytes32 _requestId = _dummyRequestIds[0]; + (bytes32 _requestId,) = _mockRequest(); IOracle.Request memory _request = oracle.getRequest(_requestId); _request.resolutionModule = IResolutionModule(address(0)); @@ -973,26 +1129,9 @@ contract Oracle_UnitTest is Test { // Test: try to resolve the dispute oracle.resolveDispute(_disputeId); } +} - /** - * @notice update dispute status revert if sender not resolution module - */ - function test_updateDisputeStatusRevertIfCallerNotResolutionModule(uint256 _newStatus) public { - // 0 to 3 status, fuzzed - _newStatus = bound(_newStatus, 0, 3); - - // Store mock request - _storeDummyRequests(1); - bytes32 _disputeId = bytes32('69'); - - // Check: revert? - vm.expectRevert(abi.encodeWithSelector(IOracle.Oracle_NotResolutionModule.selector, sender)); - - // Test: try to update the status from an EOA - vm.prank(sender); - oracle.updateDisputeStatus(_disputeId, IOracle.DisputeStatus(_newStatus)); - } - +contract Unit_AllowedModule is BaseTest { /** * @notice Test if allowed module returns correct bool for the modules */ @@ -1005,8 +1144,7 @@ contract Oracle_UnitTest is Test { ); // Create mock request and store it - this uses the 5 modules globally defined - (bytes32[] memory _dummyRequestIds,) = _storeDummyRequests(1); - bytes32 _requestId = _dummyRequestIds[0]; + (bytes32 _requestId,) = _mockRequest(); // Check: the correct modules are recognized as valid assertTrue(oracle.allowedModule(_requestId, address(requestModule))); @@ -1018,52 +1156,67 @@ contract Oracle_UnitTest is Test { // Check: any other address is not recognized as allowed module assertFalse(oracle.allowedModule(_requestId, _notAModule)); } +} + +contract Unit_IsParticipant is BaseTest { + /** + * @notice Test if an address is a participant + */ + function test_isParticipant(bytes32 _requestId, address _notParticipant) public { + vm.assume(_notParticipant != requester && _notParticipant != proposer && _notParticipant != disputer); + + // Set valid participants + oracle.forTest_addParticipant(_requestId, requester); + oracle.forTest_addParticipant(_requestId, proposer); + oracle.forTest_addParticipant(_requestId, disputer); + + // Check: the participants are recognized + assertTrue(oracle.isParticipant(_requestId, requester)); + assertTrue(oracle.isParticipant(_requestId, proposer)); + assertTrue(oracle.isParticipant(_requestId, disputer)); + + // Check: any other address is not recognized as a participant + assertFalse(oracle.isParticipant(_requestId, _notParticipant)); + } +} + +contract Unit_GetFinalizedResponseId is BaseTest { + /** + * @notice Test if the finalized response id is returned correctly + */ + function test_getFinalizedResponseId(bytes32 _requestId, bytes32 _finalizedResponseId) public { + assertEq(oracle.getFinalizedResponseId(_requestId), bytes32(0)); + oracle.forTest_setFinalizedResponseId(_requestId, _finalizedResponseId); + assertEq(oracle.getFinalizedResponseId(_requestId), _finalizedResponseId); + } +} +contract Unit_Finalize is BaseTest { /** * @notice Test finalize mocks and expects call * * @dev The request might or might not use a dispute and a finality module, this is fuzzed */ - function test_finalize(bool _useResolutionAndFinality, address _caller) public { + function test_finalize( + bool _useResolutionAndFinality, + address _caller + ) public setResolutionAndFinality(_useResolutionAndFinality) { // Create mock request and store it - (bytes32[] memory _dummyRequestIds,) = _storeDummyRequests(1); - - bytes32 _requestId = _dummyRequestIds[0]; - IOracle.Response memory _response = IOracle.Response({ - createdAt: block.timestamp, - proposer: _caller, - requestId: _requestId, - disputeId: bytes32(0), - response: bytes('69') - }); - - bytes32 _responseId = oracle.forTest_setResponse(_response); - - if (!_useResolutionAndFinality) { - disputeModule = IDisputeModule(address(0)); - finalityModule = IFinalityModule(address(0)); - } + (bytes32 _requestId,) = _mockRequest(); - // mock and expect the finalize request in requestModule - vm.mockCall(address(requestModule), abi.encodeCall(IModule.finalizeRequest, (_requestId, _caller)), abi.encode()); - vm.expectCall(address(requestModule), abi.encodeCall(IModule.finalizeRequest, (_requestId, _caller))); + mockResponse.proposer = _caller; + mockResponse.requestId = _requestId; - // mock and expect the finalize request in responseModule - vm.mockCall(address(responseModule), abi.encodeCall(IModule.finalizeRequest, (_requestId, _caller)), abi.encode()); - vm.expectCall(address(responseModule), abi.encodeCall(IModule.finalizeRequest, (_requestId, _caller))); + bytes32 _responseId = oracle.forTest_setResponse(mockResponse); + bytes memory _calldata = abi.encodeCall(IModule.finalizeRequest, (_requestId, _caller)); - // mock and expect the finalize request in resolutionModule - vm.mockCall(address(resolutionModule), abi.encodeCall(IModule.finalizeRequest, (_requestId, _caller)), abi.encode()); - vm.expectCall(address(resolutionModule), abi.encodeCall(IModule.finalizeRequest, (_requestId, _caller))); + _mockAndExpect(address(requestModule), _calldata, abi.encode()); + _mockAndExpect(address(responseModule), _calldata, abi.encode()); + _mockAndExpect(address(disputeModule), _calldata, abi.encode()); if (_useResolutionAndFinality) { - // mock and expect the call to disputeModule - vm.mockCall(address(disputeModule), abi.encodeCall(IModule.finalizeRequest, (_requestId, _caller)), abi.encode()); - vm.expectCall(address(disputeModule), abi.encodeCall(IModule.finalizeRequest, (_requestId, _caller))); - - // mock and expect the call to finalityModule - vm.mockCall(address(finalityModule), abi.encodeCall(IModule.finalizeRequest, (_requestId, _caller)), abi.encode()); - vm.expectCall(address(finalityModule), abi.encodeCall(IModule.finalizeRequest, (_requestId, _caller))); + _mockAndExpect(address(resolutionModule), _calldata, abi.encode()); + _mockAndExpect(address(finalityModule), _calldata, abi.encode()); } // Check: emits OracleRequestFinalized event? @@ -1075,35 +1228,25 @@ contract Oracle_UnitTest is Test { oracle.finalize(_requestId, _responseId); } - function test_finalizeRevertsWhenInvalidFinalizedResponse(address _caller) public { + function test_finalizeRevertsWhenInvalidFinalizedResponse(address _caller, bytes32 _disputeId) public { // Create mock request and store it - (bytes32[] memory _dummyRequestIds,) = _storeDummyRequests(1); - bytes32 _requestId = _dummyRequestIds[0]; - - address _proposer = makeAddr('proposer'); - - bytes32 _disputeId = bytes32('69'); + (bytes32 _requestId,) = _mockRequest(); // Create mock response and store it - IOracle.Response memory _response = IOracle.Response({ - createdAt: block.timestamp, - proposer: _proposer, - requestId: _requestId, - disputeId: _disputeId, - response: bytes('69') - }); + mockResponse.requestId = _requestId; + mockResponse.disputeId = _disputeId; - bytes32 _responseId = oracle.forTest_setResponse(_response); + bytes32 _responseId = oracle.forTest_setResponse(mockResponse); // Dispute the response - vm.mockCall( + _mockAndExpect( address(disputeModule), - abi.encodeCall(IDisputeModule.disputeResponse, (_requestId, _responseId, sender, _proposer)), + abi.encodeCall(IDisputeModule.disputeResponse, (_requestId, _responseId, disputer, proposer)), abi.encode(mockDispute) ); // Test: dispute the response - vm.prank(sender); + vm.prank(disputer); oracle.disputeResponse(_requestId, _responseId); // Test: finalize the request with active dispute reverts @@ -1134,36 +1277,38 @@ contract Oracle_UnitTest is Test { vm.expectRevert(abi.encodeWithSelector(IOracle.Oracle_InvalidFinalizedResponse.selector, _responseId)); vm.prank(_caller); oracle.finalize(_requestId, _responseId); + + // Override the finalizedAt to make it be finalized + IOracle.Request memory _request = oracle.getRequest(_requestId); + _request.finalizedAt = _request.createdAt; + oracle.forTest_setRequest(_requestId, _request); + + // Test: finalize a finalized request + vm.expectRevert(abi.encodeWithSelector(IOracle.Oracle_AlreadyFinalized.selector, _requestId)); + vm.prank(_caller); + oracle.finalize(_requestId, _responseId); } function test_finalizeRevertsInvalidRequestId(address _caller) public { // Create mock request and store it - (bytes32[] memory _dummyRequestIds,) = _storeDummyRequests(2); - bytes32 _requestId = _dummyRequestIds[0]; - bytes32 _incorrectRequestId = _dummyRequestIds[1]; - - address _proposer = makeAddr('proposer'); + (bytes32[] memory _mockRequestIds,) = _mockRequests(2); + bytes32 _requestId = _mockRequestIds[0]; + bytes32 _incorrectRequestId = _mockRequestIds[1]; // Create mock response and store it - IOracle.Response memory _response = IOracle.Response({ - createdAt: block.timestamp, - proposer: _proposer, - requestId: _requestId, - disputeId: bytes32('69'), - response: bytes('69') - }); + mockResponse.requestId = _requestId; - bytes32 _responseId = oracle.forTest_setResponse(_response); + bytes32 _responseId = oracle.forTest_setResponse(mockResponse); // Dispute the response - vm.mockCall( + _mockAndExpect( address(disputeModule), - abi.encodeCall(IDisputeModule.disputeResponse, (_requestId, _responseId, sender, _proposer)), + abi.encodeCall(IDisputeModule.disputeResponse, (_requestId, _responseId, disputer, proposer)), abi.encode(mockDispute) ); // Test: dispute the response - vm.prank(sender); + vm.prank(disputer); oracle.disputeResponse(_requestId, _responseId); vm.expectRevert(abi.encodeWithSelector(IOracle.Oracle_InvalidFinalizedResponse.selector, _responseId)); @@ -1178,37 +1323,90 @@ contract Oracle_UnitTest is Test { * * @dev The request might or might not use a dispute and a finality module, this is fuzzed */ - function test_finalize_withoutResponses(bool _useResolutionAndFinality, address _caller) public { + function test_finalize_withoutResponses( + bool _useResolutionAndFinality, + address _caller + ) public setResolutionAndFinality(_useResolutionAndFinality) { // Create mock request and store it - (bytes32[] memory _dummyRequestIds,) = _storeDummyRequests(1); + (bytes32 _requestId,) = _mockRequest(); + bytes memory _calldata = abi.encodeCall(IModule.finalizeRequest, (_requestId, _caller)); - bytes32 _requestId = _dummyRequestIds[0]; + _mockAndExpect(address(requestModule), _calldata, abi.encode()); + _mockAndExpect(address(responseModule), _calldata, abi.encode()); + _mockAndExpect(address(resolutionModule), _calldata, abi.encode()); - if (!_useResolutionAndFinality) { - disputeModule = IDisputeModule(address(0)); - finalityModule = IFinalityModule(address(0)); + if (_useResolutionAndFinality) { + _mockAndExpect(address(disputeModule), _calldata, abi.encode()); + _mockAndExpect(address(finalityModule), _calldata, abi.encode()); } - // mock and expect the finalize request in requestModule - vm.mockCall(address(requestModule), abi.encodeCall(IModule.finalizeRequest, (_requestId, _caller)), abi.encode()); - vm.expectCall(address(requestModule), abi.encodeCall(IModule.finalizeRequest, (_requestId, _caller))); + // Check: emits OracleRequestFinalized event? + vm.expectEmit(true, true, true, true); + emit OracleRequestFinalized(_requestId, _caller); + + // Test: finalize the request + vm.prank(_caller); + oracle.finalize(_requestId); + + // Override the finalizedAt to make it be finalized + IOracle.Request memory _request = oracle.getRequest(_requestId); + _request.finalizedAt = _request.createdAt; + oracle.forTest_setRequest(_requestId, _request); + + // Test: finalize a finalized request + vm.expectRevert(abi.encodeWithSelector(IOracle.Oracle_AlreadyFinalized.selector, _requestId)); + vm.prank(_caller); + oracle.finalize(_requestId); + } + + function test_finalizeRequest_withDisputedResponse(bytes32 _responseId, bytes32 _disputeId) public { + // Create mock request and store it + (bytes32 _requestId,) = _mockRequest(); - // mock and expect the finalize request in responseModule - vm.mockCall(address(responseModule), abi.encodeCall(IModule.finalizeRequest, (_requestId, _caller)), abi.encode()); - vm.expectCall(address(responseModule), abi.encodeCall(IModule.finalizeRequest, (_requestId, _caller))); + // Test: finalize a request with a disputed response + for (uint256 _i; _i < uint256(type(IOracle.DisputeStatus).max); _i++) { + // Any status but None and Lost reverts + if (_i == uint256(IOracle.DisputeStatus.None) || _i == uint256(IOracle.DisputeStatus.Lost)) { + continue; + } - // mock and expect the finalize request in resolutionModule - vm.mockCall(address(resolutionModule), abi.encodeCall(IModule.finalizeRequest, (_requestId, _caller)), abi.encode()); - vm.expectCall(address(resolutionModule), abi.encodeCall(IModule.finalizeRequest, (_requestId, _caller))); + // Mocking a response that has a dispute with the given status + mockDispute.status = IOracle.DisputeStatus(_i); + oracle.forTest_addResponseId(_requestId, _responseId); + oracle.forTest_setDisputeOf(_responseId, _disputeId); + oracle.forTest_setDispute(_disputeId, mockDispute); - if (_useResolutionAndFinality) { - // mock and expect the call to disputeModule - vm.mockCall(address(disputeModule), abi.encodeCall(IModule.finalizeRequest, (_requestId, _caller)), abi.encode()); - vm.expectCall(address(disputeModule), abi.encodeCall(IModule.finalizeRequest, (_requestId, _caller))); + vm.expectRevert(abi.encodeWithSelector(IOracle.Oracle_InvalidFinalizedResponse.selector, _responseId)); + vm.prank(requester); + oracle.finalize(_requestId); - // mock and expect the call to finalityModule - vm.mockCall(address(finalityModule), abi.encodeCall(IModule.finalizeRequest, (_requestId, _caller)), abi.encode()); - vm.expectCall(address(finalityModule), abi.encodeCall(IModule.finalizeRequest, (_requestId, _caller))); + // Resetting the response ids to start from scratch + oracle.forTest_removeResponseId(_requestId, _responseId); + } + } + + /** + * @notice Test finalize mocks and expects call + * + * @dev The request might or might not use a dispute and a finality module, this is fuzzed + */ + function test_finalize_disputedResponse( + bool _useResolutionAndFinality, + address _caller + ) public setResolutionAndFinality(_useResolutionAndFinality) { + // Create mock request and store it + (bytes32 _requestId,) = _mockRequest(); + + // Mock and expect the finalizeRequest call on the required modules + bytes memory _calldata = abi.encodeCall(IModule.finalizeRequest, (_requestId, _caller)); + _mockAndExpect(address(requestModule), _calldata, abi.encode()); + _mockAndExpect(address(responseModule), _calldata, abi.encode()); + _mockAndExpect(address(disputeModule), _calldata, abi.encode()); + + // If needed, mock and expect the finalizeRequest call on the resolution and finality modules + if (_useResolutionAndFinality) { + _mockAndExpect(address(resolutionModule), _calldata, abi.encode()); + _mockAndExpect(address(finalityModule), _calldata, abi.encode()); } // Check: emits OracleRequestFinalized event? @@ -1219,33 +1417,33 @@ contract Oracle_UnitTest is Test { vm.prank(_caller); oracle.finalize(_requestId); } +} +contract Unit_TotalRequestCount is BaseTest { function test_totalRequestCount(uint256 _requestsToAdd) public { _requestsToAdd = bound(_requestsToAdd, 1, 10); uint256 _initialCount = oracle.totalRequestCount(); - _storeDummyRequests(_requestsToAdd); + _mockRequests(_requestsToAdd); assert(oracle.totalRequestCount() == _initialCount + _requestsToAdd); } +} - function test_escalateDispute() public { +contract Unit_EscalateDispute is BaseTest { + function test_escalateDispute(bytes32 _disputeId) public { // Create mock request and store it - (bytes32[] memory _dummyRequestIds,) = _storeDummyRequests(1); - bytes32 _requestId = _dummyRequestIds[0]; + (bytes32 _requestId,) = _mockRequest(); // Create a dummy dispute - bytes32 _disputeId = bytes32('69'); mockDispute.requestId = _requestId; oracle.forTest_setDispute(_disputeId, mockDispute); // Mock and expect the resolution module call - vm.mockCall( + _mockAndExpect( address(resolutionModule), abi.encodeCall(IResolutionModule.startResolution, (_disputeId)), abi.encode() ); - vm.expectCall(address(resolutionModule), abi.encodeCall(IResolutionModule.startResolution, (_disputeId))); // Mock and expect the dispute module call - vm.mockCall(address(disputeModule), abi.encodeCall(IDisputeModule.disputeEscalated, (_disputeId)), abi.encode()); - vm.expectCall(address(disputeModule), abi.encodeCall(IDisputeModule.disputeEscalated, (_disputeId))); + _mockAndExpect(address(disputeModule), abi.encodeCall(IDisputeModule.disputeEscalated, (_disputeId)), abi.encode()); // Expect dispute escalated event vm.expectEmit(true, true, true, true); @@ -1258,20 +1456,18 @@ contract Oracle_UnitTest is Test { assertEq(uint256(_dispute.status), uint256(IOracle.DisputeStatus.Escalated)); } - function test_escalateDisputeNoResolutionModule() public { + function test_escalateDisputeNoResolutionModule(bytes32 _disputeId) public { // Create mock request and store it - (bytes32[] memory _dummyRequestIds,) = _storeDummyRequests(1); - bytes32 _requestId = _dummyRequestIds[0]; + (bytes32 _requestId,) = _mockRequest(); + oracle.forTest_setResolutionModule(_requestId, address(0)); // Create a dummy dispute - bytes32 _disputeId = bytes32('69'); mockDispute.requestId = _requestId; oracle.forTest_setDispute(_disputeId, mockDispute); // Mock and expect the dispute module call - vm.mockCall(address(disputeModule), abi.encodeCall(IDisputeModule.disputeEscalated, (_disputeId)), abi.encode()); - vm.expectCall(address(disputeModule), abi.encodeCall(IDisputeModule.disputeEscalated, (_disputeId))); + _mockAndExpect(address(disputeModule), abi.encodeCall(IDisputeModule.disputeEscalated, (_disputeId)), abi.encode()); // Expect dispute escalated event vm.expectEmit(true, true, true, true); @@ -1284,26 +1480,18 @@ contract Oracle_UnitTest is Test { assertEq(uint256(_dispute.status), uint256(IOracle.DisputeStatus.Escalated)); } - function test_escalateDisputeRevertsIfDisputeNotValid() public { - // Create mock request and store it - _storeDummyRequests(1); - - // Create a dummy dispute - bytes32 _disputeId = bytes32('69'); - + function test_escalateDisputeRevertsIfDisputeNotValid(bytes32 _disputeId) public { vm.expectRevert(abi.encodeWithSelector(IOracle.Oracle_InvalidDisputeId.selector, _disputeId)); // Test: escalate the dispute oracle.escalateDispute(_disputeId); } - function test_escalateDisputeRevertsIfDisputeNotActive() public { + function test_escalateDisputeRevertsIfDisputeNotActive(bytes32 _disputeId) public { // Create mock request and store it - (bytes32[] memory _dummyRequestIds,) = _storeDummyRequests(1); - bytes32 _requestId = _dummyRequestIds[0]; + (bytes32 _requestId,) = _mockRequest(); // Create a dummy dispute - bytes32 _disputeId = bytes32('69'); mockDispute.requestId = _requestId; mockDispute.status = IOracle.DisputeStatus.None; oracle.forTest_setDispute(_disputeId, mockDispute); @@ -1313,71 +1501,4 @@ contract Oracle_UnitTest is Test { // Test: escalate the dispute oracle.escalateDispute(_disputeId); } - - /** - * @notice create mock requests and store them in the oracle - * - * @dev each request has an incremental ipfsHash in order to easily test their content - * - * @param _howMany uint256 how many request to store - * - * @return _requestIds bytes32[] the request ids - */ - function _storeDummyRequests(uint256 _howMany) - internal - returns (bytes32[] memory _requestIds, IOracle.NewRequest[] memory _requests) - { - _requestIds = new bytes32[](_howMany); - _requests = new IOracle.NewRequest[](_howMany); - - for (uint256 _i; _i < _howMany; _i++) { - IOracle.NewRequest memory _request = IOracle.NewRequest({ - requestModuleData: bytes('requestModuleData'), - responseModuleData: bytes('responseModuleData'), - disputeModuleData: bytes('disputeModuleData'), - resolutionModuleData: bytes('resolutionModuleData'), - finalityModuleData: bytes('finalityModuleData'), - ipfsHash: bytes32(_i), - requestModule: requestModule, - responseModule: responseModule, - disputeModule: disputeModule, - resolutionModule: resolutionModule, - finalityModule: finalityModule - }); - - vm.prank(sender); - _requestIds[_i] = oracle.createRequest(_request); - _requests[_i] = _request; - - vm.mockCall( - address(requestModule), - abi.encodeCall(IModule.requestData, (_requestIds[_i])), - abi.encode(_request.requestModuleData) - ); - - vm.mockCall( - address(responseModule), - abi.encodeCall(IModule.requestData, (_requestIds[_i])), - abi.encode(_request.responseModuleData) - ); - - vm.mockCall( - address(disputeModule), - abi.encodeCall(IModule.requestData, (_requestIds[_i])), - abi.encode(_request.disputeModuleData) - ); - - vm.mockCall( - address(resolutionModule), - abi.encodeCall(IModule.requestData, (_requestIds[_i])), - abi.encode(_request.resolutionModuleData) - ); - - vm.mockCall( - address(finalityModule), - abi.encodeCall(IModule.requestData, (_requestIds[_i])), - abi.encode(_request.finalityModuleData) - ); - } - } }