Skip to content

Commit

Permalink
test: integration tests for releasing unutilized response
Browse files Browse the repository at this point in the history
  • Loading branch information
gas1cent committed Nov 13, 2024
1 parent 6dde53d commit edc81f1
Show file tree
Hide file tree
Showing 2 changed files with 169 additions and 54 deletions.
54 changes: 0 additions & 54 deletions solidity/test/integration/Finalization.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -156,60 +156,6 @@ contract Integration_Finalization is IntegrationBase {
assertEq(_finalizedResponseId, bytes32(0));
}

/**
* @notice Release unutilized response bond after finalization.
*/
function test_releaseUnutilizedResponse(bytes calldata _calldata) public {
// Create request, response and dispute it
bytes32 _requestId = _createRequest();
mockResponse.requestId = _requestId;
bytes32 _responseId = _proposeResponse();
mockDispute.requestId = _requestId;
mockDispute.responseId = _responseId;
bytes32 _disputeId = _disputeResponse();

mockResponse.requestId = bytes32(0);
IOracle.Response memory _emptyResponse = mockResponse;
mockResponse.requestId = _requestId;

// After the deadline has passed, finalize with an empty response
vm.warp(block.timestamp + _expectedDeadline);
vm.prank(_finalizer);
oracle.finalize(mockRequest, _emptyResponse, _createAccessControl());

// Check: is request finalized with an empty response?
assertEq(oracle.finalizedResponseId(_requestId), bytes32(0));
assertEq(oracle.finalizedAt(_requestId), block.timestamp);

// Check: proposers funds are still bonded?
assertEq(_accountingExtension.bondedAmountOf(proposer, usdc, _requestId), _expectedBondSize);

// Escalate dispute
vm.prank(disputer);
oracle.escalateDispute(mockRequest, mockResponse, mockDispute, _createAccessControl());

vm.mockCall(
address(_mockArbitrator),
abi.encodeCall(IArbitrator.getAnswer, (_disputeId)),
abi.encode(IOracle.DisputeStatus.Lost)
);

// Second step: resolving the dispute
vm.prank(disputer);
oracle.resolveDispute(mockRequest, mockResponse, mockDispute, _createAccessControl());

// Check: proposers funds are still bonded?
assertEq(_accountingExtension.bondedAmountOf(proposer, usdc, _requestId), _expectedBondSize);

// Now the proposer should be able to release their unused response
vm.prank(proposer);
_responseModule.releaseUnutilizedResponse(mockRequest, mockResponse);

// Check: proposers funds are still bonded?
assertEq(_accountingExtension.bondedAmountOf(proposer, usdc, _requestId), 0);
assertEq(_accountingExtension.balanceOf(proposer, usdc), _expectedBondSize * 2);
}

/**
* @notice Updates the finalization module and its data.
*/
Expand Down
169 changes: 169 additions & 0 deletions solidity/test/integration/ReleaseUnutilizedResponse.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import './IntegrationBase.sol';

contract Integration_ReleaseUnutilizedResponse is IntegrationBase {
address internal _finalizer = makeAddr('finalizer');
bytes32 _requestId;

Check warning on line 8 in solidity/test/integration/ReleaseUnutilizedResponse.t.sol

View workflow job for this annotation

GitHub Actions / Run Linters (16.x)

Explicitly mark visibility of state
bytes32 _responseId;

Check warning on line 9 in solidity/test/integration/ReleaseUnutilizedResponse.t.sol

View workflow job for this annotation

GitHub Actions / Run Linters (16.x)

Explicitly mark visibility of state
bytes32 _disputeId;

Check warning on line 10 in solidity/test/integration/ReleaseUnutilizedResponse.t.sol

View workflow job for this annotation

GitHub Actions / Run Linters (16.x)

Explicitly mark visibility of state

function setUp() public override {
super.setUp();

_deposit(_accountingExtension, requester, usdc, _expectedReward);
_deposit(_accountingExtension, proposer, usdc, _expectedBondSize);
_deposit(_accountingExtension, disputer, usdc, _expectedBondSize);

// Create request, response and dispute it
_requestId = _createRequest();
mockResponse.requestId = _requestId;

_responseId = _proposeResponse(mockResponse);
mockDispute.requestId = _requestId;
mockDispute.responseId = _responseId;

_disputeId = _disputeResponse();
}

/**
* @notice Tests the release of unutilized response when the request is finalized with an empty response
*/
function test_releaseUnutilizedResponse_withoutResponse() public {
mockResponse.requestId = bytes32(0);
IOracle.Response memory _emptyResponse = mockResponse;
mockResponse.requestId = _requestId;

// Finalizing the request with an empty response
_finalizeRequest(_emptyResponse);

// Check: is request finalized with an empty response?
assertEq(oracle.finalizedResponseId(_requestId), bytes32(0));
assertEq(oracle.finalizedAt(_requestId), block.timestamp);

// Check: proposers funds are still bonded?
assertEq(_accountingExtension.bondedAmountOf(proposer, usdc, _requestId), _expectedBondSize, 'Balance check 1');

// The dispute is escalated and has no resolution
_escalateAndResolveDispute(IOracle.DisputeStatus.NoResolution);

// Check: proposers funds are still bonded?
assertEq(_accountingExtension.bondedAmountOf(proposer, usdc, _requestId), _expectedBondSize, 'Balance check 2');

// Now the proposer should be able to release their unused response
vm.prank(proposer);
_responseModule.releaseUnutilizedResponse(mockRequest, mockResponse);

// Check: proposers funds are not bonded anymore?
assertEq(_accountingExtension.bondedAmountOf(proposer, usdc, _requestId), 0, 'Balance check 3');

// Check: proposer received the disputer's bond?
assertEq(_accountingExtension.balanceOf(proposer, usdc), _expectedBondSize * 2);
}

/**
* @notice Tests the release of unutilized response when the request is finalized with a correct response
*/
function test_releaseUnutilizedResponse_withResponse() public {
IOracle.Response memory _unutilizedResponse = mockResponse;

IOracle.Response memory _acceptedResponse = mockResponse;
_acceptedResponse.response = bytes('123');
_deposit(_accountingExtension, proposer, usdc, _expectedBondSize);
_proposeResponse(_acceptedResponse);

// Check: impossible to release responses before the request is finalized
vm.expectRevert(IBondedResponseModule.BondedResponseModule_InvalidReleaseParameters.selector);
_responseModule.releaseUnutilizedResponse(mockRequest, _unutilizedResponse);

// Finalizing the request with a correct response
_finalizeRequest(_acceptedResponse);

// Check: impossible to release utilized responses
vm.expectRevert(IBondedResponseModule.BondedResponseModule_InvalidReleaseParameters.selector);
_responseModule.releaseUnutilizedResponse(mockRequest, _acceptedResponse);

// Check: is request finalized with an empty response?
assertEq(oracle.finalizedResponseId(_requestId), _getId(_acceptedResponse));
assertEq(oracle.finalizedAt(_requestId), block.timestamp);

// Check: proposers funds are still bonded?
assertEq(_accountingExtension.bondedAmountOf(proposer, usdc, _requestId), _expectedBondSize, 'Balance check 1');

// The dispute is escalated and has no resolution
_escalateAndResolveDispute(IOracle.DisputeStatus.Lost);

// Check: proposers funds are still bonded?
assertEq(_accountingExtension.bondedAmountOf(proposer, usdc, _requestId), _expectedBondSize, 'Balance check 2');

// Now the proposer should be able to release their unused response
vm.prank(proposer);
_responseModule.releaseUnutilizedResponse(mockRequest, _unutilizedResponse);

// Check: proposers funds are not bonded anymore?
assertEq(_accountingExtension.bondedAmountOf(proposer, usdc, _requestId), 0, 'Balance check 3');

// Check: proposer received
// - their own bonds for 2 responses
// - disputer's bond for winning the dispute
// - the reward for the correct response
assertEq(_accountingExtension.balanceOf(proposer, usdc), _expectedBondSize * 3 + _expectedReward);
}

/**
* @notice Tests reverts that happen when trying to release a response that is being or that has been successfully disputed
*/
function test_releaseUnutilizedResponse_withDisputedResponse() public {
mockResponse.requestId = bytes32(0);
IOracle.Response memory _emptyResponse = mockResponse;
mockResponse.requestId = _requestId;

// Finalizing the request with an empty response
_finalizeRequest(_emptyResponse);

// Check: is request finalized with an empty response?
assertEq(oracle.finalizedResponseId(_requestId), bytes32(0));
assertEq(oracle.finalizedAt(_requestId), block.timestamp);

// Check: impossible to release the disputed response while the dispute is active
vm.expectRevert(IBondedResponseModule.BondedResponseModule_InvalidReleaseParameters.selector);
_responseModule.releaseUnutilizedResponse(mockRequest, mockResponse);

// The dispute is escalated and has no resolution
_escalateAndResolveDispute(IOracle.DisputeStatus.Won);

// Check: impossible to release the disputed response if the disputer won
vm.expectRevert(IBondedResponseModule.BondedResponseModule_InvalidReleaseParameters.selector);
_responseModule.releaseUnutilizedResponse(mockRequest, mockResponse);

// Check: proposers bond is slashed
assertEq(_accountingExtension.bondedAmountOf(proposer, usdc, _requestId), 0);
}

function _escalateAndResolveDispute(IOracle.DisputeStatus _status) internal {
// Escalate dispute
vm.prank(disputer);
oracle.escalateDispute(mockRequest, mockResponse, mockDispute, _createAccessControl());

// Resolve with the provided status
vm.mockCall(address(_mockArbitrator), abi.encodeCall(IArbitrator.getAnswer, (_disputeId)), abi.encode(_status));

vm.prank(disputer);
oracle.resolveDispute(mockRequest, mockResponse, mockDispute, _createAccessControl());
}

function _finalizeRequest(IOracle.Response memory _response) internal {
// After the deadline has passed, finalize with the given response
vm.warp(block.timestamp + _expectedDeadline);
vm.prank(_finalizer);
oracle.finalize(mockRequest, _response, _createAccessControl());
}

function _proposeResponse(IOracle.Response memory _response) internal returns (bytes32 _responseId) {
vm.startPrank(proposer);
_accountingExtension.approveModule(address(_responseModule));
_responseId = oracle.proposeResponse(mockRequest, _response, _createAccessControl());
vm.stopPrank();
}
}

0 comments on commit edc81f1

Please sign in to comment.