Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: access control #52

Merged
merged 20 commits into from
Nov 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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(
ashitakah marked this conversation as resolved.
Show resolved Hide resolved
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);
xorsal marked this conversation as resolved.
Show resolved Hide resolved
_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;
ashitakah marked this conversation as resolved.
Show resolved Hide resolved
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
0xJabberwock marked this conversation as resolved.
Show resolved Hide resolved
)
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(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need the msg.senders in finalize? Is there a use for it? If not sure, can you ask in the prophet channel what the intended use is?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

commented in the discord channel

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be meaningful to revise what each existing implementation of the Prophet modules expects as to the _finalizer parameter of finalizeRequest().

  • Apparently, all dispute and resolution modules don't utilize it.
  • Moreover, all request and finality modules only do it upon the emission of RequestFinalized event (except for EBORequestModule).
  • However, the unique response module does so with the aim of identifying whether the argument is an Oracle::allowedModule, apart from for the event emission.
  • Lastly, mind that CircuitResolverModule and RootVerificationModule (both dispute modules) and EBO's CouncilArbitrator (not a module) call Oracle::finalize.

IOracle.Request calldata _request,
IOracle.Response calldata _response,
AccessControl calldata _accessControl
0xJabberwock marked this conversation as resolved.
Show resolved Hide resolved
) 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) {
ashitakah marked this conversation as resolved.
Show resolved Hide resolved
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
Loading