Skip to content

Commit

Permalink
feat: propose from dispute module (#40)
Browse files Browse the repository at this point in the history
  • Loading branch information
gas1cent authored Jul 7, 2023
1 parent 64daadc commit bbb2f03
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 19 deletions.
36 changes: 20 additions & 16 deletions solidity/contracts/Oracle.sol
Original file line number Diff line number Diff line change
Expand Up @@ -91,25 +91,29 @@ contract Oracle is IOracle {
_dispute = _disputes[_disputeId];
}

function getProposers(bytes32 _requestId) external view returns (address[] memory _proposers) {
bytes32[] memory _responsesIds = _responseIds[_requestId];
if (_responsesIds.length == 0) return _proposers;
_proposers = new address[](_responsesIds.length);

for (uint256 _i; _i < _responsesIds.length;) {
_proposers[_i] = _responses[_responsesIds[_i]].proposer;

unchecked {
++_i;
}
}
}

function proposeResponse(bytes32 _requestId, bytes calldata _responseData) external returns (bytes32 _responseId) {
Request memory _request = _requests[_requestId];
_responseId = _proposeResponse(msg.sender, _requestId, _request, _responseData);
}

_responseId = keccak256(abi.encodePacked(msg.sender, address(this), _requestId, _responseNonce++));
_responses[_responseId] = _request.responseModule.propose(_requestId, msg.sender, _responseData);
function proposeResponse(
address _proposer,
bytes32 _requestId,
bytes calldata _responseData
) external returns (bytes32 _responseId) {
Request memory _request = _requests[_requestId];
if (msg.sender != address(_request.disputeModule)) revert Oracle_NotDisputeModule(msg.sender);
_responseId = _proposeResponse(_proposer, _requestId, _request, _responseData);
}

function _proposeResponse(
address _proposer,
bytes32 _requestId,
Request memory _request,
bytes calldata _responseData
) internal returns (bytes32 _responseId) {
_responseId = keccak256(abi.encodePacked(_proposer, address(this), _requestId, _responseNonce++));
_responses[_responseId] = _request.responseModule.propose(_requestId, _proposer, _responseData);
_responseIds[_requestId].push(_responseId);
}

Expand Down
1 change: 0 additions & 1 deletion solidity/contracts/modules/BondEscalationModule.sol
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,6 @@ contract BondEscalationModule is Module, IBondEscalationModule {
escalatedDispute[_requestId] = _disputeId;
}

// TODO: Check if can dispute
_dispute = IOracle.Dispute({
disputer: _disputer,
responseId: _responseId,
Expand Down
1 change: 0 additions & 1 deletion solidity/contracts/modules/BondedDisputeModule.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ contract BondedDisputeModule is Module, IBondedDisputeModule {
address _disputer,
address _proposer
) external onlyOracle returns (IOracle.Dispute memory _dispute) {
// TODO: Check if can dispute
_dispute = IOracle.Dispute({
disputer: _disputer,
responseId: _responseId,
Expand Down
7 changes: 6 additions & 1 deletion solidity/interfaces/IOracle.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {IFinalityModule} from './modules/IFinalityModule.sol';
interface IOracle {
/// @notice Thrown when the caller of the slash() function is not the DisputeModule
error Oracle_NotResolutionModule(address _caller);
error Oracle_NotDisputeModule(address _caller);

error Oracle_ResponseAlreadyDisputed(bytes32 _responseId);
error Oracle_AlreadyFinalized(bytes32 _requestId);
Expand Down Expand Up @@ -83,13 +84,17 @@ interface IOracle {
function getRequest(bytes32 _requestId) external view returns (Request memory _request);
function disputeOf(bytes32 _requestId) external view returns (bytes32 _disputeId);
function proposeResponse(bytes32 _requestId, bytes calldata _responseData) external returns (bytes32 _responseId);
function proposeResponse(
address _proposer,
bytes32 _requestId,
bytes calldata _responseData
) external returns (bytes32 _responseId);
function disputeResponse(bytes32 _requestId, bytes32 _responseId) external returns (bytes32 _disputeId);
function escalateDispute(bytes32 _disputeId) external;
function getFinalizedResponse(bytes32 _requestId) external view returns (Response memory _response);
function getResponseIds(bytes32 _requestId) external view returns (bytes32[] memory _ids);
function resolveDispute(bytes32 _disputeId) external;
function updateDisputeStatus(bytes32 _disputeId, DisputeStatus _status) external;
function getProposers(bytes32 _requestId) external view returns (address[] memory _proposers);
function listRequests(uint256 _startFrom, uint256 _amount) external view returns (Request[] memory _list);
function listRequestIds(uint256 _startFrom, uint256 _batchSize) external view returns (bytes32[] memory _list);
function finalize(bytes32 _requestId, bytes32 _finalizedResponseId) external;
Expand Down
69 changes: 69 additions & 0 deletions solidity/test/unit/Oracle.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,75 @@ contract Oracle_UnitTest is Test {
assertEq(_responseIds[1], _secondResponseId);
}

/**
* @notice Test dispute module proposes a response as somebody else: check _responses, _responseIds and _responseId
*/
function test_proposeResponseWithProposer(address _proposer, bytes calldata _responseData) public {
vm.assume(_proposer != address(0));

// Create mock request and store it
bytes32 _requestId = _storeDummyRequests(1)[0];

// Get the current response nonce (8th slot)
uint256 _responseNonce = uint256(vm.load(address(oracle), bytes32(uint256(0x8))));

// 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
});

// Test: revert if called by a random dude (not dispute module)
vm.expectRevert(abi.encodeWithSelector(IOracle.Oracle_NotDisputeModule.selector, sender));
vm.prank(sender);
oracle.proposeResponse(_proposer, _requestId, _responseData);

// Mock&expect the responseModule propose call:
vm.mockCall(
address(responseModule),
abi.encodeCall(IResponseModule.propose, (_requestId, _proposer, _responseData)),
abi.encode(_response)
);
vm.expectCall(
address(responseModule), abi.encodeCall(IResponseModule.propose, (_requestId, _proposer, _responseData))
);

// Test: propose the response
vm.prank(address(disputeModule));
bytes32 _actualResponseId = oracle.proposeResponse(_proposer, _requestId, _responseData);

vm.prank(address(disputeModule));
bytes32 _secondResponseId = oracle.proposeResponse(_proposer, _requestId, _responseData);

// Check: correct response id returned?
assertEq(_actualResponseId, _responseId);

// Check: responseId are unique?
assertNotEq(_secondResponseId, _responseId);

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);

bytes32[] memory _responseIds = oracle.getResponseIds(_requestId);

// Check: correct response id stored in the id list and unique?
assertEq(_responseIds.length, 2);
assertEq(_responseIds[0], _responseId);
assertEq(_responseIds[1], _secondResponseId);
}

/**
* @notice Test dispute response: check _responses, _responseIds and _responseId
*/
Expand Down

0 comments on commit bbb2f03

Please sign in to comment.