Skip to content

Commit d9ce30a

Browse files
committed
feat: modifier and tests
1 parent 7fd9bbf commit d9ce30a

File tree

9 files changed

+234
-66
lines changed

9 files changed

+234
-66
lines changed

solidity/contracts/Oracle.sol

Lines changed: 42 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ import {
1818
_ESCALATE_TYPEHASH,
1919
_FINALIZE_TYPEHASH,
2020
_PROPOSE_TYPEHASH,
21-
_RESOLVE_TYPEHASH,
2221
_UPDATE_TYPEHASH
2322
} from './utils/OracleTypehash.sol';
2423

@@ -66,6 +65,18 @@ contract Oracle is IOracle, AccessController {
6665
*/
6766
mapping(bytes32 _requestId => bytes _responseIds) internal _responseIds;
6867

68+
/**
69+
* @notice Modifier to check if the user approved to the access control module
70+
* @param _user The address of the user
71+
* @param _accessControlModule The access control module to check if approved
72+
*/
73+
modifier isApproved(address _user, address _accessControlModule) {
74+
if (_accessControlModule != address(0) && !isAccessControlApproved[_user][_accessControlModule]) {
75+
revert Oracle_AccessControlModuleNotApproved();
76+
}
77+
_;
78+
}
79+
6980
/// @inheritdoc IOracle
7081
function getResponseIds(bytes32 _requestId) public view returns (bytes32[] memory _ids) {
7182
bytes memory _responses = _responseIds[_requestId];
@@ -107,6 +118,13 @@ contract Oracle is IOracle, AccessController {
107118
}
108119
}
109120

121+
/// @inheritdoc IOracle
122+
function setAccessControlModule(address _accessControlModule, bool _approved) external {
123+
isAccessControlApproved[msg.sender][_accessControlModule] = _approved;
124+
125+
emit AccessControlModuleSet(msg.sender, _accessControlModule, _approved);
126+
}
127+
110128
/// @inheritdoc IOracle
111129
function createRequest(
112130
Request calldata _request,
@@ -125,7 +143,7 @@ contract Oracle is IOracle, AccessController {
125143
uint256 _requestsAmount = _requestsData.length;
126144
_batchRequestsIds = new bytes32[](_requestsAmount);
127145

128-
for (uint256 _i = 0; _i < _requestsAmount;) {
146+
for (uint256 _i; _i < _requestsAmount;) {
129147
_batchRequestsIds[_i] = _createRequest(_requestsData[_i], _ipfsHashes[_i], _accessControl[_i]);
130148
unchecked {
131149
++_i;
@@ -140,6 +158,7 @@ contract Oracle is IOracle, AccessController {
140158
AccessControl calldata _accessControl
141159
)
142160
external
161+
isApproved(_accessControl.user, _request.accessControlModule)
143162
hasAccess(_request.accessControlModule, _PROPOSE_TYPEHASH, abi.encode(_request, _response), _accessControl)
144163
returns (bytes32 _responseId)
145164
{
@@ -180,6 +199,7 @@ contract Oracle is IOracle, AccessController {
180199
AccessControl calldata _accessControl
181200
)
182201
external
202+
isApproved(_accessControl.user, _request.accessControlModule)
183203
hasAccess(_request.accessControlModule, _DISPUTE_TYPEHASH, abi.encode(_request, _response, _dispute), _accessControl)
184204
returns (bytes32 _disputeId)
185205
{
@@ -225,6 +245,7 @@ contract Oracle is IOracle, AccessController {
225245
AccessControl calldata _accessControl
226246
)
227247
external
248+
isApproved(_accessControl.user, _request.accessControlModule)
228249
hasAccess(_request.accessControlModule, _ESCALATE_TYPEHASH, abi.encode(_request, _response, _dispute), _accessControl)
229250
{
230251
(bytes32 _responseId, bytes32 _disputeId) = ValidatorLib._validateResponseAndDispute(_request, _response, _dispute);
@@ -256,15 +277,7 @@ contract Oracle is IOracle, AccessController {
256277
}
257278

258279
/// @inheritdoc IOracle
259-
function resolveDispute(
260-
Request calldata _request,
261-
Response calldata _response,
262-
Dispute calldata _dispute,
263-
AccessControl calldata _accessControl
264-
)
265-
external
266-
hasAccess(_request.accessControlModule, _RESOLVE_TYPEHASH, abi.encode(_request, _response, _dispute), _accessControl)
267-
{
280+
function resolveDispute(Request calldata _request, Response calldata _response, Dispute calldata _dispute) external {
268281
(bytes32 _responseId, bytes32 _disputeId) = ValidatorLib._validateResponseAndDispute(_request, _response, _dispute);
269282

270283
if (disputeCreatedAt[_disputeId] == 0) {
@@ -292,13 +305,14 @@ contract Oracle is IOracle, AccessController {
292305

293306
/// @inheritdoc IOracle
294307
function updateDisputeStatus(
295-
Request calldata _request,
296-
Response calldata _response,
297-
Dispute calldata _dispute,
308+
Request memory _request,
309+
Response memory _response,
310+
Dispute memory _dispute,
298311
DisputeStatus _status,
299312
AccessControl calldata _accessControl
300313
)
301314
external
315+
isApproved(_accessControl.user, _request.accessControlModule)
302316
hasAccess(
303317
_request.accessControlModule,
304318
_UPDATE_TYPEHASH,
@@ -316,16 +330,24 @@ contract Oracle is IOracle, AccessController {
316330
revert Oracle_InvalidDisputeId(_disputeId);
317331
}
318332

333+
// Needed to avoid stack too deep when try to compile
334+
Request memory _currentRequest = _request;
335+
Response memory _currentResponse = _response;
336+
Dispute memory _currentDispute = _dispute;
337+
DisputeStatus _currentStatus = _status;
338+
319339
if (
320-
_accessControl.user != address(_request.disputeModule)
321-
&& _accessControl.user != address(_request.resolutionModule)
340+
_accessControl.user != address(_currentRequest.disputeModule)
341+
&& _accessControl.user != address(_currentRequest.resolutionModule)
322342
) {
323343
revert Oracle_NotDisputeOrResolutionModule(_accessControl.user);
324344
}
325-
disputeStatus[_disputeId] = _status;
326-
IDisputeModule(_request.disputeModule).onDisputeStatusChange(_disputeId, _request, _response, _dispute);
345+
disputeStatus[_disputeId] = _currentStatus;
346+
IDisputeModule(_currentRequest.disputeModule).onDisputeStatusChange(
347+
_disputeId, _currentRequest, _currentResponse, _currentDispute
348+
);
327349

328-
emit DisputeStatusUpdated(_disputeId, _dispute, _status);
350+
emit DisputeStatusUpdated(_disputeId, _currentDispute, _currentStatus);
329351
}
330352

331353
/// @inheritdoc IOracle
@@ -445,6 +467,7 @@ contract Oracle is IOracle, AccessController {
445467
AccessControl calldata _accessControl
446468
)
447469
internal
470+
isApproved(_accessControl.user, _request.accessControlModule)
448471
hasAccess(_request.accessControlModule, _CREATE_TYPEHASH, abi.encode(_request), _accessControl)
449472
returns (bytes32 _requestId)
450473
{

solidity/contracts/utils/OracleTypehash.sol

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@ bytes32 constant _DISPUTE_TYPEHASH = keccak256('DisputeResponse(Request _request
1010

1111
bytes32 constant _ESCALATE_TYPEHASH = keccak256('EscalateDispute(Request _request,Response _response,Dispute _dispute)');
1212

13-
bytes32 constant _RESOLVE_TYPEHASH = keccak256('ResolveDispute(Request _request,Response _response,Dispute _dispute)');
14-
1513
bytes32 constant _UPDATE_TYPEHASH =
1614
keccak256('UpdateDisputeStatus(Request _request,Response _response,Dispute _dispute,DisputeStatus _status)');
1715

solidity/interfaces/IOracle.sol

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,14 @@ interface IOracle is IAccessController {
1212
EVENTS
1313
//////////////////////////////////////////////////////////////*/
1414

15+
/**
16+
* @notice Emitted when the access control module is set
17+
* @param _user The address of the user
18+
* @param _accessControlModule The address of the access control module
19+
* @param _approved If the module is approved
20+
*/
21+
event AccessControlModuleSet(address indexed _user, address indexed _accessControlModule, bool _approved);
22+
1523
/**
1624
* @notice Emitted when a request is created
1725
* @param _requestId The id of the created request
@@ -70,6 +78,11 @@ interface IOracle is IAccessController {
7078
ERRORS
7179
//////////////////////////////////////////////////////////////*/
7280

81+
/**
82+
* @notice Thrown when user didn't approve the access control module
83+
*/
84+
error Oracle_AccessControlModuleNotApproved();
85+
7386
/**
7487
* @notice Thrown when an unauthorized caller is trying to change a dispute's status
7588
* @param _caller The caller of the function
@@ -370,6 +383,14 @@ interface IOracle is IAccessController {
370383
LOGIC
371384
//////////////////////////////////////////////////////////////*/
372385

386+
/**
387+
* @notice Sets the address of the access control module
388+
*
389+
* @param _accessControlModule The address of the access control module
390+
* @param _approved If the module is approved
391+
*/
392+
function setAccessControlModule(address _accessControlModule, bool _approved) external;
393+
373394
/**
374395
* @notice Generates the request ID and initializes the modules for the request
375396
*
@@ -450,14 +471,8 @@ interface IOracle is IAccessController {
450471
* @param _request The request
451472
* @param _response The disputed response
452473
* @param _dispute The dispute that is being resolved
453-
* @param _accessControl The access control data
454474
*/
455-
function resolveDispute(
456-
Request calldata _request,
457-
Response calldata _response,
458-
Dispute calldata _dispute,
459-
AccessControl calldata _accessControl
460-
) external;
475+
function resolveDispute(Request calldata _request, Response calldata _response, Dispute calldata _dispute) external;
461476

462477
/**
463478
* @notice Updates the status of a dispute

solidity/test/integration/EscalateDispute.t.sol

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,19 @@ contract Integration_EscalateDispute is IntegrationBase {
1414
oracle.proposeResponse(mockRequest, mockResponse, mockAccessControl);
1515
vm.stopPrank();
1616

17-
// Dispute reverts if caller is not authorized
18-
vm.startPrank(badCaller);
19-
vm.expectRevert(abi.encodeWithSelector(IAccessController.AccessControlData_NoAccess.selector));
17+
// Revert if not approved to dispute
18+
address _badDisputer = makeAddr('badDisputer');
19+
mockAccessControl.user = _badDisputer;
20+
21+
// Reverts because caller is not approved to dispute
22+
vm.expectRevert(abi.encodeWithSelector(IOracle.Oracle_AccessControlModuleNotApproved.selector));
23+
oracle.disputeResponse(mockRequest, mockResponse, mockDispute, mockAccessControl);
24+
25+
// Dispute reverts if caller is not approved
2026
mockAccessControl.user = disputer;
27+
vm.expectRevert(abi.encodeWithSelector(IAccessController.AccessControlData_NoAccess.selector));
28+
vm.prank(badCaller);
2129
oracle.disputeResponse(mockRequest, mockResponse, mockDispute, mockAccessControl);
22-
vm.stopPrank();
2330

2431
// Dispute the response
2532
vm.startPrank(caller);

solidity/test/integration/IntegrationBase.sol

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -149,9 +149,10 @@ contract IntegrationBase is TestConstants, Helpers {
149149
mockDispute.responseId = _getId(mockResponse);
150150

151151
// Add the allowed callers to the access control module
152-
address[] memory _allowedCallers = new address[](1);
153-
_allowedCallers[0] = caller;
154-
_accessControlModule.setHasAccess(_allowedCallers);
152+
_setAccessControlForACaller(requester, caller);
153+
_setAccessControlForACaller(proposer, caller);
154+
_setAccessControlForACaller(disputer, caller);
155+
_setAccessControlForACaller(finalizer, caller);
155156

156157
vm.startPrank(caller);
157158
}
@@ -164,4 +165,13 @@ contract IntegrationBase is TestConstants, Helpers {
164165
vm.warp(block.timestamp + _blocks * BLOCK_TIME);
165166
vm.roll(block.number + _blocks);
166167
}
168+
169+
function _setAccessControlForACaller(address _delegator, address _caller) internal {
170+
vm.startPrank(_delegator);
171+
address[] memory _allowedCallers = new address[](1);
172+
_allowedCallers[0] = _caller;
173+
_accessControlModule.setHasAccess(_allowedCallers);
174+
oracle.setAccessControlModule(address(_accessControlModule), true);
175+
vm.stopPrank();
176+
}
167177
}

solidity/test/integration/ResponseDispute.t.sol

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,19 +30,28 @@ contract Integration_ResponseDispute is IntegrationBase {
3030
mockAccessControl.user = finalizer;
3131
oracle.finalize(mockRequest, mockResponse, mockAccessControl);
3232

33+
// Revert if the dispute is already finalized
3334
mockAccessControl.user = disputer;
3435
vm.expectRevert(abi.encodeWithSelector(IOracle.Oracle_AlreadyFinalized.selector, _requestId));
3536
oracle.disputeResponse(mockRequest, mockResponse, mockDispute, mockAccessControl);
3637
}
3738

3839
function test_disputeResponse_alreadyDisputed() public {
40+
// Dispute the response
3941
mockAccessControl.user = disputer;
4042
oracle.disputeResponse(mockRequest, mockResponse, mockDispute, mockAccessControl);
4143

4244
address _anotherDisputer = makeAddr('anotherDisputer');
4345
mockDispute.disputer = _anotherDisputer;
44-
4546
mockAccessControl.user = _anotherDisputer;
47+
48+
// Revert if the response is access control is not approved
49+
vm.expectRevert(abi.encodeWithSelector(IOracle.Oracle_AccessControlModuleNotApproved.selector));
50+
oracle.disputeResponse(mockRequest, mockResponse, mockDispute, mockAccessControl);
51+
52+
// Revert if the response is already disputed
53+
mockAccessControl.user = disputer;
54+
mockDispute.disputer = disputer;
4655
vm.expectRevert(abi.encodeWithSelector(IOracle.Oracle_ResponseAlreadyDisputed.selector, _responseId));
4756
oracle.disputeResponse(mockRequest, mockResponse, mockDispute, mockAccessControl);
4857
}

solidity/test/integration/ResponseProposal.t.sol

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,48 @@ contract Integration_ResponseProposal is IntegrationBase {
88

99
function setUp() public override {
1010
super.setUp();
11-
vm.stopPrank();
1211

1312
mockRequest.nonce = uint96(oracle.totalRequestCount());
14-
mockAccessControl.user = requester;
1513

16-
vm.startPrank(badCaller);
17-
vm.expectRevert(abi.encodeWithSelector(IAccessController.AccessControlData_NoAccess.selector));
14+
address _badRequester = makeAddr('badRequester');
15+
mockAccessControl.user = _badRequester;
16+
17+
// Revert if not approved to create request
18+
vm.expectRevert(abi.encodeWithSelector(IOracle.Oracle_AccessControlModuleNotApproved.selector));
1819
oracle.createRequest(mockRequest, _ipfsHash, mockAccessControl);
20+
1921
vm.stopPrank();
2022

23+
// Revert if has no access to create request
24+
mockAccessControl.user = requester;
25+
vm.expectRevert(abi.encodeWithSelector(IAccessController.AccessControlData_NoAccess.selector));
26+
vm.prank(badCaller);
27+
oracle.createRequest(mockRequest, _ipfsHash, mockAccessControl);
28+
29+
// Create request
2130
vm.startPrank(caller);
2231
_requestId = oracle.createRequest(mockRequest, _ipfsHash, mockAccessControl);
2332
}
2433

2534
function test_proposeResponse_validResponse(bytes memory _response) public {
2635
mockResponse.response = _response;
2736

37+
// Revert if not approved to create request
38+
vm.expectRevert(abi.encodeWithSelector(IOracle.Oracle_AccessControlModuleNotApproved.selector));
39+
address _badProposer = makeAddr('badProposer');
40+
mockAccessControl.user = _badProposer;
41+
oracle.proposeResponse(mockRequest, mockResponse, mockAccessControl);
42+
43+
vm.stopPrank();
44+
45+
// Revert if has no access to create request
46+
mockAccessControl.user = proposer;
47+
vm.expectRevert(abi.encodeWithSelector(IAccessController.AccessControlData_NoAccess.selector));
48+
vm.prank(badCaller);
49+
oracle.proposeResponse(mockRequest, mockResponse, mockAccessControl);
50+
51+
vm.startPrank(caller);
52+
// Propose response
2853
mockAccessControl.user = proposer;
2954
oracle.proposeResponse(mockRequest, mockResponse, mockAccessControl);
3055

@@ -38,13 +63,16 @@ contract Integration_ResponseProposal is IntegrationBase {
3863
function test_proposeResponse_finalizedRequest(uint256 _timestamp) public {
3964
_timestamp = bound(_timestamp, _expectedDeadline + _baseDisputeWindow, type(uint128).max);
4065

66+
// Propose response
4167
mockAccessControl.user = proposer;
4268
oracle.proposeResponse(mockRequest, mockResponse, mockAccessControl);
4369

70+
// Finalize request
4471
vm.warp(_timestamp);
4572
mockAccessControl.user = finalizer;
4673
oracle.finalize(mockRequest, mockResponse, mockAccessControl);
4774

75+
// Revert if the request is already finalized
4876
mockAccessControl.user = proposer;
4977
mockResponse.response = abi.encode(_timestamp);
5078
vm.expectRevert(abi.encodeWithSelector(IOracle.Oracle_AlreadyFinalized.selector, _requestId));

solidity/test/mocks/contracts/MockAtomicArbitrator.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ contract MockAtomicArbitrator {
1818
) external returns (bytes memory _result) {
1919
_result = new bytes(0);
2020
answer = IOracle.DisputeStatus.Won;
21-
// oracle.resolveDispute(_request, _response, _dispute);
21+
oracle.resolveDispute(_request, _response, _dispute);
2222
}
2323

2424
function getAnswer(bytes32 /* _dispute */ ) external view returns (IOracle.DisputeStatus _answer) {

0 commit comments

Comments
 (0)