Skip to content

Commit

Permalink
feat: access control (#52)
Browse files Browse the repository at this point in the history
Co-authored-by: xorsal <[email protected]>
Co-authored-by: Gas One Cent <[email protected]>
  • Loading branch information
3 people authored Nov 12, 2024
1 parent 96d1084 commit 0e46031
Show file tree
Hide file tree
Showing 21 changed files with 1,282 additions and 290 deletions.
34 changes: 34 additions & 0 deletions solidity/contracts/CommonAccessController.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import {IAccessController} from '../interfaces/IAccessController.sol';
import {IAccessModule} from '../interfaces/modules/access/IAccessModule.sol';

abstract contract CommonAccessController is IAccessController {
/**
* @notice Check whether the caller is authorized for the given parameters.
* @param _accessModule The access module
* @param _typehash The typehash
* @param _typehashParams The params passed to the typehash
* @param _accessControl The access control struct
*/
function _hasAccess(
address _accessModule,
bytes32 _typehash,
bytes memory _typehashParams,
AccessControl memory _accessControl
) internal {
bool _granted = IAccessModule(_accessModule).hasAccess(
abi.encode(
IAccessModule.AccessControlParameters({
sender: msg.sender,
accessControl: _accessControl,
typehash: _typehash,
typehashParams: _typehashParams
})
)
);

if (!_granted) revert AccessController_NoAccess();
}
}
178 changes: 114 additions & 64 deletions solidity/contracts/Oracle.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,17 @@ pragma solidity ^0.8.19;

import {IOracle} from '../interfaces/IOracle.sol';

import {IAccessModule} from '../interfaces/modules/access/IAccessModule.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 '../libraries/ValidatorLib.sol';
import {OracleAccessController} from './OracleAccessController.sol';
import {OracleTypehash} from './utils/OracleTypehash.sol';

contract Oracle is IOracle {
contract Oracle is IOracle, OracleAccessController, OracleTypehash {
using ValidatorLib for *;

/// @inheritdoc IOracle
Expand Down Expand Up @@ -44,31 +46,26 @@ contract Oracle is IOracle {
/// @inheritdoc IOracle
mapping(bytes32 _requestId => mapping(address _user => bool _isParticipant)) public isParticipant;

/// @inheritdoc IOracle
uint256 public totalRequestCount;

/**
* @notice The list of the response ids for each request
*/
mapping(bytes32 _requestId => bytes _responseIds) internal _responseIds;

/// @inheritdoc IOracle
uint256 public totalRequestCount;

/// @inheritdoc IOracle
function createRequest(Request calldata _request, bytes32 _ipfsHash) external returns (bytes32 _requestId) {
_requestId = _createRequest(_request, _ipfsHash);
}
function getResponseIds(bytes32 _requestId) public view returns (bytes32[] memory _ids) {
bytes memory _responses = _responseIds[_requestId];
uint256 _length = _responses.length / 32;

/// @inheritdoc IOracle
function createRequests(
Request[] calldata _requestsData,
bytes32[] calldata _ipfsHashes
) external returns (bytes32[] memory _batchRequestsIds) {
uint256 _requestsAmount = _requestsData.length;
_batchRequestsIds = new bytes32[](_requestsAmount);
assembly {
for { let _i := 0 } lt(_i, _length) { _i := add(_i, 1) } {
// Increase the size of the array
mstore(_ids, add(mload(_ids), 1))

for (uint256 _i = 0; _i < _requestsAmount;) {
_batchRequestsIds[_i] = _createRequest(_requestsData[_i], _ipfsHashes[_i]);
unchecked {
++_i;
// Store the response id in the array
mstore(add(_ids, add(32, mul(_i, 32))), mload(add(_responses, add(32, mul(_i, 32)))))
}
}
}
Expand Down Expand Up @@ -98,11 +95,42 @@ contract Oracle is IOracle {
}
}

/// @inheritdoc IOracle
function createRequest(
Request calldata _request,
bytes32 _ipfsHash,
AccessControl calldata _accessControl
) external returns (bytes32 _requestId) {
_requestId = _createRequest(_request, _ipfsHash, _accessControl);
}

/// @inheritdoc IOracle
function createRequests(
Request[] calldata _requestsData,
bytes32[] calldata _ipfsHashes,
AccessControl[] calldata _accessControl
) external returns (bytes32[] memory _batchRequestsIds) {
uint256 _requestsAmount = _requestsData.length;
_batchRequestsIds = new bytes32[](_requestsAmount);

for (uint256 _i; _i < _requestsAmount;) {
_batchRequestsIds[_i] = _createRequest(_requestsData[_i], _ipfsHashes[_i], _accessControl[_i]);
unchecked {
++_i;
}
}
}

/// @inheritdoc IOracle
function proposeResponse(
Request calldata _request,
Response calldata _response
) external returns (bytes32 _responseId) {
Response calldata _response,
AccessControl calldata _accessControl
)
external
hasAccess(_request.accessModule, PROPOSE_TYPEHASH, abi.encode(_request, _response), _accessControl)
returns (bytes32 _responseId)
{
_responseId = ValidatorLib._validateResponse(_request, _response);

bytes32 _requestId = _response.requestId;
Expand All @@ -111,8 +139,8 @@ contract Oracle is IOracle {
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)) {
// The user must be the proposer unless response comes from the dispute module
if (_accessControl.user != _response.proposer && _accessControl.user != _request.disputeModule) {
revert Oracle_InvalidProposer();
}

Expand All @@ -125,7 +153,7 @@ contract Oracle is IOracle {
revert Oracle_AlreadyFinalized(_requestId);
}
isParticipant[_requestId][_response.proposer] = true;
IResponseModule(_request.responseModule).propose(_request, _response, msg.sender);
IResponseModule(_request.responseModule).propose(_request, _response, _accessControl.user);
_responseIds[_requestId] = abi.encodePacked(_responseIds[_requestId], _responseId);
responseCreatedAt[_responseId] = block.timestamp;

Expand All @@ -136,8 +164,13 @@ contract Oracle is IOracle {
function disputeResponse(
Request calldata _request,
Response calldata _response,
Dispute calldata _dispute
) external returns (bytes32 _disputeId) {
Dispute calldata _dispute,
AccessControl calldata _accessControl
)
external
hasAccess(_request.accessModule, DISPUTE_TYPEHASH, abi.encode(_request, _response, _dispute), _accessControl)
returns (bytes32 _disputeId)
{
bytes32 _responseId;
(_responseId, _disputeId) = ValidatorLib._validateResponseAndDispute(_request, _response, _dispute);

Expand All @@ -151,7 +184,7 @@ contract Oracle is IOracle {
revert Oracle_InvalidProposer();
}

if (_dispute.disputer != msg.sender) {
if (_dispute.disputer != _accessControl.user) {
revert Oracle_InvalidDisputer();
}

Expand All @@ -162,7 +195,7 @@ contract Oracle is IOracle {
if (disputeOf[_responseId] != bytes32(0)) {
revert Oracle_ResponseAlreadyDisputed(_responseId);
}
isParticipant[_requestId][msg.sender] = true;
isParticipant[_requestId][_accessControl.user] = true;
disputeStatus[_disputeId] = DisputeStatus.Active;
disputeOf[_responseId] = _disputeId;
disputeCreatedAt[_disputeId] = block.timestamp;
Expand All @@ -173,7 +206,15 @@ contract Oracle is IOracle {
}

/// @inheritdoc IOracle
function escalateDispute(Request calldata _request, Response calldata _response, Dispute calldata _dispute) external {
function escalateDispute(
Request calldata _request,
Response calldata _response,
Dispute calldata _dispute,
AccessControl calldata _accessControl
)
external
hasAccess(_request.accessModule, ESCALATE_TYPEHASH, abi.encode(_request, _response, _dispute), _accessControl)
{
(bytes32 _responseId, bytes32 _disputeId) = ValidatorLib._validateResponseAndDispute(_request, _response, _dispute);

if (disputeCreatedAt[_disputeId] == 0) {
Expand All @@ -194,16 +235,24 @@ contract Oracle is IOracle {
// Notify the dispute module about the escalation
IDisputeModule(_request.disputeModule).onDisputeStatusChange(_disputeId, _request, _response, _dispute);

emit DisputeEscalated(msg.sender, _disputeId, _dispute);
emit DisputeEscalated(_accessControl.user, _disputeId, _dispute);

if (address(_request.resolutionModule) != address(0)) {
if (_request.resolutionModule != address(0)) {
// Initiate the resolution
IResolutionModule(_request.resolutionModule).startResolution(_disputeId, _request, _response, _dispute);
}
}

/// @inheritdoc IOracle
function resolveDispute(Request calldata _request, Response calldata _response, Dispute calldata _dispute) external {
function resolveDispute(
Request calldata _request,
Response calldata _response,
Dispute calldata _dispute,
AccessControl calldata _accessControl
)
external
hasAccess(_request.accessModule, RESOLVE_TYPEHASH, abi.encode(_request, _response, _dispute), _accessControl)
{
(bytes32 _responseId, bytes32 _disputeId) = ValidatorLib._validateResponseAndDispute(_request, _response, _dispute);

if (disputeCreatedAt[_disputeId] == 0) {
Expand All @@ -220,13 +269,13 @@ contract Oracle is IOracle {
revert Oracle_CannotResolve(_disputeId);
}

if (address(_request.resolutionModule) == address(0)) {
if (_request.resolutionModule == address(0)) {
revert Oracle_NoResolutionModule(_disputeId);
}

IResolutionModule(_request.resolutionModule).resolveDispute(_disputeId, _request, _response, _dispute);

emit DisputeResolved(_disputeId, _dispute, msg.sender);
emit DisputeResolved(_disputeId, _dispute);
}

/// @inheritdoc IOracle
Expand All @@ -246,7 +295,7 @@ contract Oracle is IOracle {
revert Oracle_InvalidDisputeId(_disputeId);
}

if (msg.sender != address(_request.disputeModule) && msg.sender != address(_request.resolutionModule)) {
if (msg.sender != _request.disputeModule && msg.sender != _request.resolutionModule) {
revert Oracle_NotDisputeOrResolutionModule(msg.sender);
}
disputeStatus[_disputeId] = _status;
Expand All @@ -256,23 +305,11 @@ contract Oracle is IOracle {
}

/// @inheritdoc IOracle
function getResponseIds(bytes32 _requestId) public view returns (bytes32[] memory _ids) {
bytes memory _responses = _responseIds[_requestId];
uint256 _length = _responses.length / 32;

assembly {
for { let _i := 0 } lt(_i, _length) { _i := add(_i, 1) } {
// Increase the size of the array
mstore(_ids, add(mload(_ids), 1))

// Store the response id in the array
mstore(add(_ids, add(32, mul(_i, 32))), mload(add(_responses, add(32, mul(_i, 32)))))
}
}
}

/// @inheritdoc IOracle
function finalize(IOracle.Request calldata _request, IOracle.Response calldata _response) external {
function finalize(
IOracle.Request calldata _request,
IOracle.Response calldata _response,
AccessControl calldata _accessControl
) external hasAccess(_request.accessModule, FINALIZE_TYPEHASH, abi.encode(_request, _response), _accessControl) {
bytes32 _requestId;
bytes32 _responseId;

Expand All @@ -289,19 +326,23 @@ contract Oracle is IOracle {

finalizedAt[_requestId] = block.timestamp;

if (address(_request.finalityModule) != address(0)) {
IFinalityModule(_request.finalityModule).finalizeRequest(_request, _response, msg.sender);
if (_request.finalityModule != address(0)) {
IFinalityModule(_request.finalityModule).finalizeRequest(_request, _response, _accessControl.user);
}

if (address(_request.resolutionModule) != address(0)) {
IResolutionModule(_request.resolutionModule).finalizeRequest(_request, _response, msg.sender);
if (_request.resolutionModule != address(0)) {
IResolutionModule(_request.resolutionModule).finalizeRequest(_request, _response, _accessControl.user);
}

IDisputeModule(_request.disputeModule).finalizeRequest(_request, _response, msg.sender);
IResponseModule(_request.responseModule).finalizeRequest(_request, _response, msg.sender);
IRequestModule(_request.requestModule).finalizeRequest(_request, _response, msg.sender);
IDisputeModule(_request.disputeModule).finalizeRequest(_request, _response, _accessControl.user);
IResponseModule(_request.responseModule).finalizeRequest(_request, _response, _accessControl.user);
IRequestModule(_request.requestModule).finalizeRequest(_request, _response, _accessControl.user);

if (_request.accessModule != address(0)) {
IAccessModule(_request.accessModule).finalizeRequest(_request, _response, _accessControl.user);
}

emit OracleRequestFinalized(_requestId, _responseId, msg.sender);
emit OracleRequestFinalized(_requestId, _responseId);
}

/**
Expand Down Expand Up @@ -372,14 +413,23 @@ contract Oracle is IOracle {
*
* @param _request The request to be created
* @param _ipfsHash The hashed IPFS CID of the metadata json
* @param _accessControl The access control struct
* @return _requestId The id of the created request
*/
function _createRequest(Request memory _request, bytes32 _ipfsHash) internal returns (bytes32 _requestId) {
function _createRequest(
Request memory _request,
bytes32 _ipfsHash,
AccessControl calldata _accessControl
)
internal
hasAccess(_request.accessModule, CREATE_TYPEHASH, abi.encode(_request), _accessControl)
returns (bytes32 _requestId)
{
uint256 _requestNonce = totalRequestCount++;

if (_request.nonce == 0) _request.nonce = uint96(_requestNonce);

if (msg.sender != _request.requester || _requestNonce != _request.nonce) {
if (_accessControl.user != _request.requester || _requestNonce != _request.nonce) {
revert Oracle_InvalidRequestBody();
}

Expand All @@ -393,9 +443,9 @@ contract Oracle is IOracle {
allowedModule[_requestId][_request.resolutionModule] = true;
allowedModule[_requestId][_request.finalityModule] = true;

isParticipant[_requestId][msg.sender] = true;
isParticipant[_requestId][_accessControl.user] = true;

IRequestModule(_request.requestModule).createRequest(_requestId, _request.requestModuleData, msg.sender);
IRequestModule(_request.requestModule).createRequest(_requestId, _request.requestModuleData, _accessControl.user);

emit RequestCreated(_requestId, _request, _ipfsHash);
}
Expand Down
Loading

0 comments on commit 0e46031

Please sign in to comment.