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: ensure relative timestamps #64

Merged
merged 10 commits into from
Oct 11, 2024
2 changes: 1 addition & 1 deletion docs/src/content/modules/dispute/bond_escalation_module.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ The Bond Escalation Module is a contract that allows users to have the first dis
- `bondToken`: The address of the token associated with the given request.
- `bondSize`: The amount to bond to dispute or propose an answer for the given request.
- `maxNumberOfEscalations`: The maximum allowed escalations or pledges for each side during the bond escalation process.
- `bondEscalationDeadline`: The timestamp at which bond escalation process finishes when pledges are not tied.
- `bondEscalationDeadline`: The number of seconds after dispute creation required to finish the bond escalation process when pledges are not tied.
- `tyingBuffer`: The number of seconds to extend the bond escalation process to allow the losing party to tie if at the end of the initial deadline the pledges weren't tied.
- `disputeWindow`: The number of seconds disputers have to challenge the proposed response since its creation.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ The Bonded Response Module is a contract that allows users to propose a response
- `accountingExtension`: The address holding the bonded tokens. It must implement the [IAccountingExtension.sol](/solidity/interfaces/extensions/IAccountingExtension.sol/interface.IAccountingExtension.md) interface.
- `bondToken`: The ERC20 token used for bonding.
- `bondSize`: The amount of tokens the disputer must bond to be able to dispute a response.
- `deadline`: The timestamp at which the module stops accepting new responses for a request and it becomes finalizable.
- `deadline`: The number of seconds after request creation at which the module stops accepting new responses for a request and it becomes finalizable.

## 3. Key Mechanisms & Concepts

Expand Down
21 changes: 16 additions & 5 deletions solidity/contracts/modules/dispute/BondEscalationModule.sol
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,9 @@ contract BondEscalationModule is Module, IBondEscalationModule {
// Only the first dispute of a request should go through the bond escalation
// Consecutive disputes should be handled by the resolution module
if (_escalation.status == BondEscalationStatus.None) {
if (block.timestamp > _params.bondEscalationDeadline) revert BondEscalationModule_BondEscalationOver();
if (block.timestamp > ORACLE.disputeCreatedAt(_disputeId) + _params.bondEscalationDeadline) {
revert BondEscalationModule_BondEscalationOver();
}
_escalation.status = BondEscalationStatus.Active;
_escalation.disputeId = _disputeId;
emit BondEscalationStatusUpdated(_dispute.requestId, _disputeId, BondEscalationStatus.Active);
Expand All @@ -83,7 +85,9 @@ contract BondEscalationModule is Module, IBondEscalationModule {
if (_disputeStatus == IOracle.DisputeStatus.Escalated) {
// The dispute has been escalated to the Resolution module
// Make sure the bond escalation deadline has passed and update the status
if (block.timestamp <= _params.bondEscalationDeadline) revert BondEscalationModule_BondEscalationNotOver();
if (block.timestamp <= ORACLE.disputeCreatedAt(_disputeId) + _params.bondEscalationDeadline) {
revert BondEscalationModule_BondEscalationNotOver();
}

if (
_escalation.status != BondEscalationStatus.Active
Expand Down Expand Up @@ -252,7 +256,9 @@ contract BondEscalationModule is Module, IBondEscalationModule {
RequestParameters memory _params = decodeRequestData(_request.disputeModuleData);
BondEscalation storage _escalation = _escalations[_dispute.requestId];

if (block.timestamp <= _params.bondEscalationDeadline + _params.tyingBuffer) {
uint256 _disputeCreatedAt = ORACLE.disputeCreatedAt(_disputeId);
// todo: shouldn't tyingBuffer only matter when dispute's pledges are tied?
xorsal marked this conversation as resolved.
Show resolved Hide resolved
if (block.timestamp <= _disputeCreatedAt + _params.bondEscalationDeadline + _params.tyingBuffer) {
revert BondEscalationModule_BondEscalationNotOver();
}

Expand Down Expand Up @@ -290,6 +296,7 @@ contract BondEscalationModule is Module, IBondEscalationModule {
bool _forDispute
) internal view returns (RequestParameters memory _params) {
bytes32 _disputeId = _validateDispute(_request, _dispute);

BondEscalation memory _escalation = _escalations[_dispute.requestId];

if (_disputeId != _escalation.disputeId) {
Expand All @@ -298,7 +305,8 @@ contract BondEscalationModule is Module, IBondEscalationModule {

_params = decodeRequestData(_request.disputeModuleData);

if (block.timestamp > _params.bondEscalationDeadline + _params.tyingBuffer) {
uint256 _disputeCreatedAt = ORACLE.disputeCreatedAt(_disputeId);
if (block.timestamp > _disputeCreatedAt + _params.bondEscalationDeadline + _params.tyingBuffer) {
revert BondEscalationModule_BondEscalationOver();
}

Expand All @@ -317,7 +325,10 @@ contract BondEscalationModule is Module, IBondEscalationModule {
if (_numPledgersAgainstDispute > _numPledgersForDispute) revert BondEscalationModule_CanOnlySurpassByOnePledge();
}

if (block.timestamp > _params.bondEscalationDeadline && _numPledgersForDispute == _numPledgersAgainstDispute) {
if (
block.timestamp > _disputeCreatedAt + _params.bondEscalationDeadline
&& _numPledgersForDispute == _numPledgersAgainstDispute
) {
revert BondEscalationModule_CannotBreakTieDuringTyingBuffer();
}
}
Expand Down
6 changes: 3 additions & 3 deletions solidity/contracts/modules/response/BondedResponseModule.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ contract BondedResponseModule is Module, IBondedResponseModule {
RequestParameters memory _params = decodeRequestData(_request.responseModuleData);

// Cannot propose after the deadline
if (block.timestamp >= _params.deadline) revert BondedResponseModule_TooLateToPropose();
uint256 _requestCreatedAt = ORACLE.requestCreatedAt(_response.requestId);
if (block.timestamp >= _requestCreatedAt + _params.deadline) revert BondedResponseModule_TooLateToPropose();

// Cannot propose to a request with a response, unless the response is being disputed
bytes32[] memory _responseIds = ORACLE.getResponseIds(_response.requestId);
Expand Down Expand Up @@ -75,12 +76,11 @@ contract BondedResponseModule is Module, IBondedResponseModule {

bool _isModule = ORACLE.allowedModule(_response.requestId, _finalizer);

if (!_isModule && block.timestamp < _params.deadline) {
if (!_isModule && block.timestamp < ORACLE.requestCreatedAt(_response.requestId) + _params.deadline) {
revert BondedResponseModule_TooEarlyToFinalize();
}

uint256 _responseCreatedAt = ORACLE.responseCreatedAt(_getId(_response));

if (_responseCreatedAt != 0) {
if (!_isModule && block.timestamp < _responseCreatedAt + _params.disputeWindow) {
revert BondedResponseModule_TooEarlyToFinalize();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,8 @@ interface IBondEscalationModule is IDisputeModule {
* @param bondToken Address of the token associated with the given request
* @param bondSize Amount to bond to dispute or propose an answer for the given request
* @param maxNumberOfEscalations Maximum allowed escalations or pledges for each side during the bond escalation process
* @param bondEscalationDeadline Timestamp at which bond escalation process finishes when pledges are not tied
* @param bondEscalationDeadline Number of seconds after dispute creation required to
* finish the bond escalation process when pledges are not tied.
* @param tyingBuffer Number of seconds to extend the bond escalation process to allow the losing
* party to tie if at the end of the initial deadline the pledges weren't tied.
* @param disputeWindow Number of seconds disputers have to challenge the proposed response since its creation.
Expand Down
22 changes: 11 additions & 11 deletions solidity/test/integration/BondEscalation.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ contract Integration_BondEscalation is IntegrationBase {
function setUp() public override {
super.setUp();

_expectedDeadline = block.timestamp + 10 days;
_bondEscalationDeadline = block.timestamp + 5 days;
_expectedDeadline = 10 days;
_bondEscalationDeadline = 5 days;

setUpRequest();
setUpEscalation();
Expand Down Expand Up @@ -128,7 +128,7 @@ contract Integration_BondEscalation is IntegrationBase {
// Step 4: Disputer runs out of capital
// Step 5: External parties see that Disputer's dispute was wrong so they don't join to escalate
// Step 6: Proposer response's is deemed correct and final once the bond escalation window is over
vm.warp(_expectedDeadline + _tyingBuffer + 1);
vm.warp(block.timestamp + _expectedDeadline + _tyingBuffer + 1);
_bondEscalationModule.settleBondEscalation(mockRequest, mockResponse, mockDispute);

IOracle.DisputeStatus _disputeStatus = oracle.disputeStatus(_disputeId);
Expand All @@ -152,7 +152,7 @@ contract Integration_BondEscalation is IntegrationBase {
assertEq(_bondEscalationAccounting.balanceOf(disputer, usdc), 0, 'Mismatch: Disputer balance');

// Step 8: Finalize request and check balances again
vm.warp(_expectedDeadline + 1 days);
vm.warp(block.timestamp + _expectedDeadline + 1 days);
oracle.finalize(mockRequest, mockResponse);

// Test: The requester has no balance because he has paid the proposer
Expand Down Expand Up @@ -189,7 +189,7 @@ contract Integration_BondEscalation is IntegrationBase {
// External parties see that Proposer's proposal was wrong so they don't join to escalate

// Step 5: Proposer response's is deemed incorrect. The bond escalation process along with the tying buffer is terminated
vm.warp(_bondEscalationDeadline + _tyingBuffer + 1);
vm.warp(block.timestamp + _bondEscalationDeadline + _tyingBuffer + 1);
_bondEscalationModule.settleBondEscalation(mockRequest, mockResponse, mockDispute);

IOracle.DisputeStatus _disputeStatus = oracle.disputeStatus(_disputeId);
Expand Down Expand Up @@ -248,7 +248,7 @@ contract Integration_BondEscalation is IntegrationBase {
_responseId = oracle.proposeResponse(mockRequest, _thirdResponse);

// Step 11: It goes undisputed for three days, therefore it's deemed correct and final
vm.warp(_expectedDeadline + 1);
vm.warp(block.timestamp + _expectedDeadline + 1);
oracle.finalize(mockRequest, _thirdResponse);

// Test: The requester has paid out the reward
Expand Down Expand Up @@ -283,7 +283,7 @@ contract Integration_BondEscalation is IntegrationBase {

// Step 12: Two days after the deadline, the resolution module says that Another proposer's answer was correct
// So Another proposer gets paid Disputer's bond
vm.warp(_expectedDeadline + 2 days);
vm.warp(block.timestamp + _expectedDeadline + 2 days);
_mockArbitrator.setAnswer(IOracle.DisputeStatus.Lost);
oracle.resolveDispute(mockRequest, _secondResponse, _secondDispute);

Expand Down Expand Up @@ -340,7 +340,7 @@ contract Integration_BondEscalation is IntegrationBase {

// Step 4: Disputer runs out of capital
// Step 5: The tying buffer kicks in
vm.warp(_bondEscalationDeadline + 1);
vm.warp(block.timestamp + _bondEscalationDeadline + 1);

// Step 6: An external party sees that Proposer's response is incorrect, so they bond the required WETH
_deposit(_bondEscalationAccounting, _secondDisputer, usdc, _pledgeSize);
Expand Down Expand Up @@ -420,7 +420,7 @@ contract Integration_BondEscalation is IntegrationBase {
_bondEscalationModule.pledgeForDispute(mockRequest, mockDispute);

// Step 6: Proposer loses in resolution
vm.warp(_bondEscalationDeadline + 1);
vm.warp(block.timestamp + _bondEscalationDeadline + 1);
oracle.escalateDispute(mockRequest, mockResponse, mockDispute);
oracle.resolveDispute(mockRequest, mockResponse, mockDispute);

Expand Down Expand Up @@ -470,7 +470,7 @@ contract Integration_BondEscalation is IntegrationBase {

// Step 4: Disputer runs out of capital
// Step 5: The tying buffer kicks in
vm.warp(_bondEscalationDeadline + 1);
vm.warp(block.timestamp + _bondEscalationDeadline + 1);

// Step 6: An external party sees that Proposer's response is incorrect, so they bond the required WETH
_deposit(_bondEscalationAccounting, _secondDisputer, usdc, _pledgeSize);
Expand Down Expand Up @@ -531,7 +531,7 @@ contract Integration_BondEscalation is IntegrationBase {

// Step 4: Disputer runs out of capital
// Step 5: The tying buffer kicks in
vm.warp(_bondEscalationDeadline + 1);
vm.warp(block.timestamp + _bondEscalationDeadline + 1);

// Step 6: An external party sees that Proposer's response is incorrect, so they bond the required WETH
_deposit(_bondEscalationAccounting, _secondDisputer, usdc, _pledgeSize);
Expand Down
2 changes: 1 addition & 1 deletion solidity/test/integration/EscalateDispute.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ contract Integration_EscalateDispute is IntegrationBase {
);

// We escalate the dispute
_mineBlocks(_blocksDeadline + 1);
vm.warp(block.timestamp + _expectedDeadline + 1);
oracle.escalateDispute(mockRequest, mockResponse, mockDispute);

// We check that the dispute was escalated
Expand Down
6 changes: 3 additions & 3 deletions solidity/test/integration/Finalization.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ contract Integration_Finalization is IntegrationBase {
_proposeResponse();

// Traveling to the end of the dispute window
vm.warp(_expectedDeadline + 1 + _baseDisputeWindow);
vm.warp(block.timestamp + _expectedDeadline + 1 + _baseDisputeWindow);

// Check: all external calls are made?
vm.expectCall(address(_mockCallback), abi.encodeWithSelector(IProphetCallback.prophetCallback.selector, _calldata));
Expand Down Expand Up @@ -70,7 +70,7 @@ contract Integration_Finalization is IntegrationBase {
* @notice Finalizing a request with a ongoing dispute reverts.
*/
function test_revertFinalizeInDisputeWindow(uint256 _timestamp) public {
_timestamp = bound(_timestamp, block.timestamp, _expectedDeadline - _baseDisputeWindow - 1);
_timestamp = bound(_timestamp, block.timestamp, block.timestamp + _expectedDeadline - _baseDisputeWindow - 1);

_createRequest();
_proposeResponse();
Expand All @@ -96,7 +96,7 @@ contract Integration_Finalization is IntegrationBase {
_proposeResponse();

// Traveling to the end of the dispute window
vm.warp(_expectedDeadline + 1 + _baseDisputeWindow);
vm.warp(block.timestamp + _expectedDeadline + 1 + _baseDisputeWindow);

vm.expectCall(address(_mockCallback), abi.encodeWithSelector(IProphetCallback.prophetCallback.selector, _calldata));
vm.prank(_finalizer);
Expand Down
8 changes: 2 additions & 6 deletions solidity/test/integration/IntegrationBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,11 @@ contract IntegrationBase is DSTestPlus, TestConstants, Helpers {
string internal _expectedResponse = '{"ethereum":{"usd":1000}}';
uint256 internal _expectedBondSize = 100 ether;
uint256 internal _expectedReward = 30 ether;
uint256 internal _expectedDeadline;
uint256 internal _expectedCallbackValue = 42;
uint256 internal _baseDisputeWindow = 120 * BLOCK_TIME;
bytes32 internal _ipfsHash = bytes32('QmR4uiJH654k3Ta2uLLQ8r');
uint256 internal _blocksDeadline = 600;
uint256 internal _timestampDeadline = _blocksDeadline * BLOCK_TIME;
uint256 internal _baseDisputeWindow = 6 hours;
uint256 internal _expectedDeadline = 10 days;

function setUp() public virtual {
vm.createSelectFork(vm.rpcUrl('optimism'), FORK_BLOCK);
Expand Down Expand Up @@ -135,9 +134,6 @@ contract IntegrationBase is DSTestPlus, TestConstants, Helpers {
_mockArbitrator = new MockArbitrator();
vm.stopPrank();

// Set the expected deadline
_expectedDeadline = block.timestamp + _timestampDeadline;

// Configure the mock request
mockRequest.requestModuleData = abi.encode(
IHttpRequestModule.RequestParameters({
Expand Down
4 changes: 2 additions & 2 deletions solidity/test/integration/Payments.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ contract Integration_Payments is IntegrationBase {
assertEq(_accountingExtension.bondedAmountOf(proposer, usdc, _requestId), _bondSize);

// Warp to finalization time.
vm.warp(_expectedDeadline + _baseDisputeWindow);
vm.warp(block.timestamp + _expectedDeadline + _baseDisputeWindow);

// Finalize request/response
oracle.finalize(mockRequest, mockResponse);
Expand Down Expand Up @@ -66,7 +66,7 @@ contract Integration_Payments is IntegrationBase {
assertEq(_accountingExtension.bondedAmountOf(proposer, weth, _requestId), _bondSize);

// Warp to finalization time.
vm.warp(_expectedDeadline + _baseDisputeWindow);
vm.warp(block.timestamp + _expectedDeadline + _baseDisputeWindow);
// Finalize request/response.
oracle.finalize(mockRequest, mockResponse);

Expand Down
2 changes: 1 addition & 1 deletion solidity/test/integration/ResponseDispute.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ contract Integration_ResponseDispute is IntegrationBase {
* @notice Disputing a finalized response should revert
*/
function test_disputeResponse_alreadyFinalized() public {
vm.warp(_expectedDeadline + _baseDisputeWindow);
vm.warp(block.timestamp + _expectedDeadline + _baseDisputeWindow);
oracle.finalize(mockRequest, mockResponse);

vm.expectRevert(abi.encodeWithSelector(IOracle.Oracle_AlreadyFinalized.selector, _getId(mockRequest)));
Expand Down
6 changes: 3 additions & 3 deletions solidity/test/integration/ResponseProposal.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,11 @@ contract Integration_ResponseProposal is IntegrationBase {
/**
* @notice Proposing a response after the deadline reverts
*/
function test_proposeResponse_afterDeadline(uint256 _timestamp, bytes memory _responseBytes) public {
vm.assume(_timestamp > _expectedDeadline);
function test_proposeResponse_afterDeadline(uint256 _secondsAfter, bytes memory _responseBytes) public {
_secondsAfter = bound(_secondsAfter, 1, 365 days);

// Warp to timestamp after deadline
vm.warp(_timestamp);
vm.warp(block.timestamp + _expectedDeadline + _secondsAfter);

mockResponse.response = _responseBytes;

Expand Down
2 changes: 1 addition & 1 deletion solidity/test/integration/RootVerification.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ contract Integration_RootVerification is IntegrationBase {
vm.prank(proposer);
oracle.proposeResponse(mockRequest, mockResponse);

vm.warp(_expectedDeadline + _baseDisputeWindow);
vm.warp(block.timestamp + _expectedDeadline + _baseDisputeWindow);

oracle.finalize(mockRequest, mockResponse);
}
Expand Down
Loading
Loading