diff --git a/contracts/Marketplace.sol b/contracts/Marketplace.sol index 37e7bdb..bc2b03e 100644 --- a/contracts/Marketplace.sol +++ b/contracts/Marketplace.sol @@ -2,6 +2,7 @@ pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "@openzeppelin/contracts/utils/math/Math.sol"; import "./Collateral.sol"; import "./Proofs.sol"; @@ -62,20 +63,19 @@ contract Marketplace is Collateral, Proofs { require(balanceOf(msg.sender) >= collateral, "Insufficient collateral"); _lock(msg.sender, requestId); - _expectProofs(slotId, requestId, request.ask.proofProbability, request.ask.duration); + _expectProofs(slotId, request.ask.proofProbability, request.ask.duration); _submitProof(slotId, proof); slot.host = msg.sender; slot.requestId = requestId; RequestContext storage context = _context(requestId); context.slotsFilled += 1; + context.endsAt = block.timestamp + request.ask.duration; emit SlotFilled(requestId, slotIndex, slotId); if (context.slotsFilled == request.ask.slots) { context.state = RequestState.Started; context.startedAt = block.timestamp; - context.endsAt = block.timestamp + request.ask.duration; _extendLockExpiryTo(requestId, context.endsAt); - _extendProofEndTo(slotId, context.endsAt); emit RequestFulfilled(requestId); } } @@ -105,6 +105,7 @@ contract Marketplace is Collateral, Proofs { context.state == RequestState.Started) { context.state = RequestState.Failed; + context.endsAt = block.timestamp - 1; emit RequestFailed(requestId); // TODO: burn all remaining slot collateral (note: slot collateral not @@ -232,11 +233,16 @@ contract Marketplace is Collateral, Proofs { function proofEnd(bytes32 slotId) public view returns (uint256) { Slot memory slot = _slot(slotId); - uint256 end = _end(slot.requestId); - if (!_slotAcceptsProofs(slotId)) { - return end < block.timestamp ? end : block.timestamp - 1; + uint256 end = _end(slotId); + RequestContext storage context = _context(slot.requestId); + if (_slotAcceptsProofs(slotId)) { + return end; + } else { + // Calculate the earliest ending between a slot and a request. + // Request endings are set, for eg, when a request fails. + uint256 earliestEnd = Math.min(end, context.endsAt); + return Math.min(earliestEnd, block.timestamp - 1); } - return end; } function _price( @@ -260,7 +266,6 @@ contract Marketplace is Collateral, Proofs { } function state(bytes32 requestId) public view returns (RequestState) { - // TODO: add check for _isFinished if (_isCancelled(requestId)) { return RequestState.Cancelled; } else if (_isFinished(requestId)) { diff --git a/contracts/Proofs.sol b/contracts/Proofs.sol index 3ffa3e1..a6a20dd 100644 --- a/contracts/Proofs.sol +++ b/contracts/Proofs.sol @@ -20,7 +20,6 @@ contract Proofs { mapping(bytes32 => bool) private ids; mapping(bytes32 => uint256) private starts; mapping(bytes32 => uint256) private ends; - mapping(bytes32 => bytes32) private idEnds; mapping(bytes32 => uint256) private probabilities; mapping(bytes32 => uint256) private markers; mapping(bytes32 => uint256) private missed; @@ -35,21 +34,10 @@ contract Proofs { return timeout; } - function _end(bytes32 endId) internal view returns (uint256) { - uint256 end = ends[endId]; + function _end(bytes32 id) internal view returns (uint256) { + uint256 end = ends[id]; require(end > 0, "Proof ending doesn't exist"); - return ends[endId]; - } - - function _endId(bytes32 id) internal view returns (bytes32) { - bytes32 endId = idEnds[id]; - require(endId > 0, "endId for given id doesn't exist"); - return endId; - } - - function _endFromId(bytes32 id) internal view returns (uint256) { - bytes32 endId = _endId(id); - return _end(endId); + return ends[id]; } function _missed(bytes32 id) internal view returns (uint256) { @@ -67,22 +55,19 @@ contract Proofs { /// @notice Informs the contract that proofs should be expected for id /// @dev Requires that the id is not already in use /// @param id identifies the proof expectation, typically a slot id - /// @param endId Identifies the id of the proof expectation ending. Typically a request id. Different from id because the proof ending is shared amongst many ids. /// @param probability The probability that a proof should be expected /// @param duration Duration, from now, for which proofs should be expected function _expectProofs( bytes32 id, // typically slot id - bytes32 endId, // typically request id, used so that the ending is global for all slots uint256 probability, uint256 duration ) internal { require(!ids[id], "Proof id already in use"); ids[id] = true; starts[id] = block.timestamp; - ends[endId] = block.timestamp + duration; + ends[id] = block.timestamp + duration; probabilities[id] = probability; markers[id] = uint256(blockhash(block.number - 1)) % period; - idEnds[id] = endId; } function _unexpectProofs( @@ -134,14 +119,13 @@ contract Proofs { if (proofPeriod <= periodOf(starts[id])) { return (false, 0); } - uint256 end = _endFromId(id); + uint256 end = _end(id); if (proofPeriod >= periodOf(end)) { return (false, 0); } pointer = _getPointer(id, proofPeriod); bytes32 challenge = _getChallenge(pointer); uint256 probability = (probabilities[id] * (256 - downtime)) / 256; - // TODO: add test for below change isRequired = ids[id] && uint256(challenge) % probability == 0; } @@ -185,19 +169,5 @@ contract Proofs { missed[id] += 1; } - /// @notice Extends the proof end time - /// @dev The id must have a mapping to an end id, the end must exist, and the end must not have elapsed yet - /// @param id the id of the proofs to extend. Typically a slot id, the id is mapped to an endId. - /// @param ending the new end time (in seconds) - function _extendProofEndTo(bytes32 id, uint256 ending) internal { - bytes32 endId = _endId(id); - uint256 end = ends[endId]; - // TODO: create type aliases for id and endId so that _end() can return - // EndId storage and we don't need to replicate the below require here - require (end > 0, "Proof ending doesn't exist"); - require (block.timestamp <= end, "Proof already ended"); - ends[endId] = ending; - } - event ProofSubmitted(bytes32 id, bytes proof); } diff --git a/contracts/TestProofs.sol b/contracts/TestProofs.sol index aa6a37f..9cdb8fb 100644 --- a/contracts/TestProofs.sol +++ b/contracts/TestProofs.sol @@ -34,11 +34,10 @@ contract TestProofs is Proofs { function expectProofs( bytes32 id, - bytes32 endId, uint256 _probability, uint256 _duration ) public { - _expectProofs(id, endId, _probability, _duration); + _expectProofs(id, _probability, _duration); } function unexpectProofs(bytes32 id) public { @@ -68,8 +67,4 @@ contract TestProofs is Proofs { function markProofAsMissing(bytes32 id, uint256 _period) public { _markProofAsMissing(id, _period); } - - function extendProofEndTo(bytes32 id, uint256 ending) public { - _extendProofEndTo(id, ending); - } } diff --git a/test/AccountLocks.test.js b/test/AccountLocks.test.js index 42aad2e..96f4432 100644 --- a/test/AccountLocks.test.js +++ b/test/AccountLocks.test.js @@ -10,7 +10,6 @@ const { } = require("./evm") const { exampleLock } = require("./examples") const { hours } = require("./time") -const { waitUntilCancelled } = require("./marketplace") describe("Account Locks", function () { let locks @@ -202,7 +201,7 @@ describe("Account Locks", function () { }) it("fails when lock is already expired", async function () { - await waitUntilCancelled(expiry) + await advanceTimeTo(expiry) await expect(locks.extendLockExpiryTo(id, newExpiry)).to.be.revertedWith( "Lock already expired" ) diff --git a/test/Marketplace.test.js b/test/Marketplace.test.js index 6b4ee82..ee30bf8 100644 --- a/test/Marketplace.test.js +++ b/test/Marketplace.test.js @@ -178,26 +178,16 @@ describe("Marketplace", function () { }) it("is rejected when request is finished", async function () { - await waitUntilStarted( - marketplace, - request.ask.slots, - slot.request, - proof - ) - await waitUntilFinished(marketplace, slotId(slot)) + const lastSlot = await waitUntilStarted(marketplace, request, slot, proof) + await waitUntilFinished(marketplace, lastSlot) await expect( marketplace.fillSlot(slot.request, slot.index, proof) ).to.be.revertedWith("Request not accepting proofs") }) it("is rejected when request is failed", async function () { - await waitUntilStarted( - marketplace, - request.ask.slots, - slot.request, - proof - ) - await waitUntilFailed(marketplace, slot, request.ask.maxSlotLoss) + await waitUntilStarted(marketplace, request, slot, proof) + await waitUntilFailed(marketplace, request, slot) await expect( marketplace.fillSlot(slot.request, slot.index, proof) ).to.be.revertedWith("Request not accepting proofs") @@ -219,6 +209,18 @@ describe("Marketplace", function () { marketplace.fillSlot(slot.request, lastSlot, proof) ).to.be.revertedWith("Slot already filled") }) + }) + + describe("proof end", function () { + beforeEach(async function () { + switchAccount(client) + await token.approve(marketplace.address, price(request)) + await marketplace.requestStorage(request) + switchAccount(host) + await token.approve(marketplace.address, collateral) + await marketplace.deposit(collateral) + }) + it("shares proof end time for all slots in request", async function () { const lastSlot = request.ask.slots - 1 for (let i = 0; i < lastSlot; i++) { @@ -233,6 +235,39 @@ describe("Marketplace", function () { await expect((await marketplace.proofEnd(slotId(sloti))) === end) } }) + + it("sets proof end time to the request duration once filled", async function () { + await marketplace.fillSlot(slot.request, slot.index, proof) + await expect(await marketplace.proofEnd(slotId(slot))).to.be.eq( + (await currentTime()) + request.ask.duration + ) + }) + + it("sets proof end time to the past once failed", async function () { + await waitUntilStarted(marketplace, request, slot, proof) + await waitUntilFailed(marketplace, request, slot) + let slot0 = { ...slot, index: request.ask.maxSlotLoss + 1 } + const now = await currentTime() + await expect(await marketplace.proofEnd(slotId(slot0))).to.be.eq(now - 1) + }) + + it("sets proof end time to the past once cancelled", async function () { + await marketplace.fillSlot(slot.request, slot.index, proof) + await waitUntilCancelled(request) + const now = await currentTime() + await expect(await marketplace.proofEnd(slotId(slot))).to.be.eq(now - 1) + }) + + it("sets proof end time to the past once finished", async function () { + const lastSlot = await waitUntilStarted(marketplace, request, slot, proof) + await waitUntilFinished(marketplace, lastSlot) // sets proofEnd to block.timestamp - 1 + const now = await currentTime() + // the proof end time is set to block.timestamp - 1 when the contract is + // finished. in the process of calling currentTime and proofEnd, + // block.timestamp has advanced by 1, so the expected proof end time will + // be block.timestamp - 2. + await expect(await marketplace.proofEnd(slotId(slot))).to.be.eq(now - 2) + }) }) describe("freeing a slot", function () { @@ -257,48 +292,36 @@ describe("Marketplace", function () { ) }) - it("fails to free slot when finished", async function () { - await waitUntilStarted( - marketplace, - request.ask.slots, - slot.request, - proof + it("fails to free slot when cancelled", async function () { + await marketplace.fillSlot(slot.request, slot.index, proof) + await waitUntilCancelled(request) + await expect(marketplace.freeSlot(slotId(slot))).to.be.revertedWith( + "Slot not accepting proofs" ) - await waitUntilFinished(marketplace, slotId(slot)) - await expect(marketplace.freeSlot(id)).to.be.revertedWith( + }) + + it("fails to free slot when finished", async function () { + const lastSlot = await waitUntilStarted(marketplace, request, slot, proof) + await waitUntilFinished(marketplace, lastSlot) + await expect(marketplace.freeSlot(slotId(slot))).to.be.revertedWith( "Slot not accepting proofs" ) }) it("successfully frees slot", async function () { - await waitUntilStarted( - marketplace, - request.ask.slots, - slot.request, - proof - ) + await waitUntilStarted(marketplace, request, slot, proof) await expect(marketplace.freeSlot(id)).not.to.be.reverted }) it("emits event once slot is freed", async function () { - await waitUntilStarted( - marketplace, - request.ask.slots, - slot.request, - proof - ) + await waitUntilStarted(marketplace, request, slot, proof) await expect(await marketplace.freeSlot(id)) .to.emit(marketplace, "SlotFreed") .withArgs(slot.request, id) }) it("cannot get slot once freed", async function () { - await waitUntilStarted( - marketplace, - request.ask.slots, - slot.request, - proof - ) + await waitUntilStarted(marketplace, request, slot, proof) await marketplace.freeSlot(id) await expect(marketplace.slot(id)).to.be.revertedWith("Slot empty") }) @@ -315,13 +338,8 @@ describe("Marketplace", function () { }) it("pays the host", async function () { - await waitUntilStarted( - marketplace, - request.ask.slots, - slot.request, - proof - ) - await waitUntilFinished(marketplace, slotId(slot)) + const lastSlot = await waitUntilStarted(marketplace, request, slot, proof) + await waitUntilFinished(marketplace, lastSlot) const startBalance = await token.balanceOf(host.address) await marketplace.payoutSlot(slot.request, slot.index) const endBalance = await token.balanceOf(host.address) @@ -336,13 +354,8 @@ describe("Marketplace", function () { }) it("can only be done once", async function () { - await waitUntilStarted( - marketplace, - request.ask.slots, - slot.request, - proof - ) - await waitUntilFinished(marketplace, slotId(slot)) + const lastSlot = await waitUntilStarted(marketplace, request, slot, proof) + await waitUntilFinished(marketplace, lastSlot) await marketplace.payoutSlot(slot.request, slot.index) await expect( marketplace.payoutSlot(slot.request, slot.index) @@ -350,13 +363,8 @@ describe("Marketplace", function () { }) it("cannot be filled again", async function () { - await waitUntilStarted( - marketplace, - request.ask.slots, - slot.request, - proof - ) - await waitUntilFinished(marketplace, slotId(slot)) + const lastSlot = await waitUntilStarted(marketplace, request, slot, proof) + await waitUntilFinished(marketplace, lastSlot) await marketplace.payoutSlot(slot.request, slot.index) await expect(marketplace.fillSlot(slot.request, slot.index, proof)).to.be .reverted @@ -420,7 +428,7 @@ describe("Marketplace", function () { }) it("rejects withdraw when wrong account used", async function () { - await waitUntilCancelled(request.expiry) + await waitUntilCancelled(request) await expect(marketplace.withdrawFunds(slot.request)).to.be.revertedWith( "Invalid client address" ) @@ -432,7 +440,7 @@ describe("Marketplace", function () { for (let i = 0; i <= lastSlot; i++) { await marketplace.fillSlot(slot.request, i, proof) } - await waitUntilCancelled(request.expiry) + await waitUntilCancelled(request) switchAccount(client) await expect(marketplace.withdrawFunds(slot.request)).to.be.revertedWith( "Invalid state" @@ -440,7 +448,7 @@ describe("Marketplace", function () { }) it("emits event once request is cancelled", async function () { - await waitUntilCancelled(request.expiry) + await waitUntilCancelled(request) switchAccount(client) await expect(marketplace.withdrawFunds(slot.request)) .to.emit(marketplace, "RequestCancelled") @@ -448,7 +456,7 @@ describe("Marketplace", function () { }) it("withdraws to the client", async function () { - await waitUntilCancelled(request.expiry) + await waitUntilCancelled(request) switchAccount(client) const startBalance = await token.balanceOf(client.address) await marketplace.withdrawFunds(slot.request) @@ -474,7 +482,7 @@ describe("Marketplace", function () { }) it("state is Cancelled when client withdraws funds", async function () { - await waitUntilCancelled(request.expiry) + await waitUntilCancelled(request) switchAccount(client) await marketplace.withdrawFunds(slot.request) await expect(await marketplace.state(slot.request)).to.equal( @@ -483,38 +491,23 @@ describe("Marketplace", function () { }) it("changes state to Started once all slots are filled", async function () { - await waitUntilStarted( - marketplace, - request.ask.slots, - slot.request, - proof - ) + await waitUntilStarted(marketplace, request, slot, proof) await expect(await marketplace.state(slot.request)).to.equal( RequestState.Started ) }) it("state is Failed once too many slots are freed", async function () { - await waitUntilStarted( - marketplace, - request.ask.slots, - slot.request, - proof - ) - await waitUntilFailed(marketplace, slot, request.ask.maxSlotLoss) + await waitUntilStarted(marketplace, request, slot, proof) + await waitUntilFailed(marketplace, request, slot) await expect(await marketplace.state(slot.request)).to.equal( RequestState.Failed ) }) it("state is Finished once slot is paid out", async function () { - await waitUntilStarted( - marketplace, - request.ask.slots, - slot.request, - proof - ) - await waitUntilFinished(marketplace, slotId(slot)) + const lastSlot = await waitUntilStarted(marketplace, request, slot, proof) + await waitUntilFinished(marketplace, lastSlot) await marketplace.payoutSlot(slot.request, slot.index) await expect(await marketplace.state(slot.request)).to.equal( RequestState.Finished @@ -535,7 +528,7 @@ describe("Marketplace", function () { }) it("changes state to Cancelled once request is cancelled", async function () { - await waitUntilCancelled(request.expiry) + await waitUntilCancelled(request) await expect(await marketplace.state(slot.request)).to.equal( RequestState.Cancelled ) @@ -543,7 +536,7 @@ describe("Marketplace", function () { it("changes isCancelled to true once request is cancelled", async function () { await expect(await marketplace.isCancelled(slot.request)).to.be.false - await waitUntilCancelled(request.expiry) + await waitUntilCancelled(request) await expect(await marketplace.isCancelled(slot.request)).to.be.true }) @@ -556,7 +549,7 @@ describe("Marketplace", function () { it("changes isSlotCancelled to true once request is cancelled", async function () { await marketplace.fillSlot(slot.request, slot.index, proof) await expect(await marketplace.isSlotCancelled(slotId(slot))).to.be.false - await waitUntilCancelled(request.expiry) + await waitUntilCancelled(request) await expect(await marketplace.isSlotCancelled(slotId(slot))).to.be.true }) @@ -565,7 +558,7 @@ describe("Marketplace", function () { await expect(await marketplace.proofEnd(slotId(slot))).to.be.gt( await currentTime() ) - await waitUntilCancelled(request.expiry) + await waitUntilCancelled(request) await expect(await marketplace.proofEnd(slotId(slot))).to.be.lt( await currentTime() ) @@ -584,7 +577,7 @@ describe("Marketplace", function () { describe("accepting proofs", function () { it("fails when request Cancelled (isCancelled is true)", async function () { await marketplace.fillSlot(slot.request, slot.index, proof) - await waitUntilCancelled(request.expiry) + await waitUntilCancelled(request) await expect( marketplace.testAcceptsProofs(slotId(slot)) ).to.be.revertedWith("Slot not accepting proofs") @@ -592,7 +585,7 @@ describe("Marketplace", function () { it("fails when request Cancelled (state set to Cancelled)", async function () { await marketplace.fillSlot(slot.request, slot.index, proof) - await waitUntilCancelled(request.expiry) + await waitUntilCancelled(request) switchAccount(client) await marketplace.withdrawFunds(slot.request) await expect( @@ -601,26 +594,26 @@ describe("Marketplace", function () { }) it("fails when request Finished (isFinished is true)", async function () { - await waitUntilStarted( + const lastSlot = await waitUntilStarted( marketplace, - request.ask.slots, - slot.request, + request, + slot, proof ) - await waitUntilFinished(marketplace, slotId(slot)) + await waitUntilFinished(marketplace, lastSlot) await expect( marketplace.testAcceptsProofs(slotId(slot)) ).to.be.revertedWith("Slot not accepting proofs") }) it("fails when request Finished (state set to Finished)", async function () { - await waitUntilStarted( + const lastSlot = await waitUntilStarted( marketplace, - request.ask.slots, - slot.request, + request, + slot, proof ) - await waitUntilFinished(marketplace, slotId(slot)) + await waitUntilFinished(marketplace, lastSlot) await marketplace.payoutSlot(slot.request, slot.index) await expect( marketplace.testAcceptsProofs(slotId(slot)) @@ -628,12 +621,7 @@ describe("Marketplace", function () { }) it("fails when request Failed", async function () { - await waitUntilStarted( - marketplace, - request.ask.slots, - slot.request, - proof - ) + await waitUntilStarted(marketplace, request, slot, proof) for (let i = 0; i <= request.ask.maxSlotLoss; i++) { slot.index = i let id = slotId(slot) diff --git a/test/Proofs.test.js b/test/Proofs.test.js index 03e9762..af99803 100644 --- a/test/Proofs.test.js +++ b/test/Proofs.test.js @@ -14,7 +14,6 @@ const { periodic, hours, minutes } = require("./time") describe("Proofs", function () { const id = hexlify(randomBytes(32)) - const endId = hexlify(randomBytes(32)) const period = 30 * 60 const timeout = 5 const downtime = 64 @@ -36,20 +35,20 @@ describe("Proofs", function () { }) it("calculates an end time based on duration", async function () { - await proofs.expectProofs(id, endId, probability, duration) + await proofs.expectProofs(id, probability, duration) let end = (await currentTime()) + duration - expect((await proofs.end(endId)).toNumber()).to.be.closeTo(end, 1) + expect((await proofs.end(id)).toNumber()).to.be.closeTo(end, 1) }) it("does not allow ids to be reused", async function () { - await proofs.expectProofs(id, endId, probability, duration) + await proofs.expectProofs(id, probability, duration) await expect( - proofs.expectProofs(id, endId, probability, duration) + proofs.expectProofs(id, probability, duration) ).to.be.revertedWith("Proof id already in use") }) it("requires proofs with an agreed upon probability", async function () { - await proofs.expectProofs(id, endId, probability, duration) + await proofs.expectProofs(id, probability, duration) let amount = 0 for (let i = 0; i < 100; i++) { if (await proofs.isProofRequired(id)) { @@ -64,7 +63,7 @@ describe("Proofs", function () { it("requires no proofs in the start period", async function () { const startPeriod = Math.floor((await currentTime()) / period) const probability = 1 - await proofs.expectProofs(id, endId, probability, duration) + await proofs.expectProofs(id, probability, duration) while (Math.floor((await currentTime()) / period) == startPeriod) { expect(await proofs.isProofRequired(id)).to.be.false await advanceTime(Math.floor(period / 10)) @@ -73,14 +72,14 @@ describe("Proofs", function () { it("requires no proofs in the end period", async function () { const probability = 1 - await proofs.expectProofs(id, endId, probability, duration) + await proofs.expectProofs(id, probability, duration) await advanceTime(duration) expect(await proofs.isProofRequired(id)).to.be.false }) it("requires no proofs after the end time", async function () { const probability = 1 - await proofs.expectProofs(id, endId, probability, duration) + await proofs.expectProofs(id, probability, duration) await advanceTime(duration + timeout) expect(await proofs.isProofRequired(id)).to.be.false }) @@ -90,7 +89,7 @@ describe("Proofs", function () { let id2 = hexlify(randomBytes(32)) let id3 = hexlify(randomBytes(32)) for (let id of [id1, id2, id3]) { - await proofs.expectProofs(id, endId, probability, duration) + await proofs.expectProofs(id, probability, duration) } let req1, req2, req3 while (req1 === req2 && req2 === req3) { @@ -119,7 +118,7 @@ describe("Proofs", function () { } beforeEach(async function () { - await proofs.expectProofs(id, endId, probability, duration) + await proofs.expectProofs(id, probability, duration) await advanceTimeTo(periodEnd(periodOf(await currentTime()))) await waitUntilProofWillBeRequired() }) @@ -152,7 +151,7 @@ describe("Proofs", function () { const proof = hexlify(randomBytes(42)) beforeEach(async function () { - await proofs.expectProofs(id, endId, probability, duration) + await proofs.expectProofs(id, probability, duration) }) async function waitUntilProofIsRequired(id) { @@ -271,87 +270,4 @@ describe("Proofs", function () { await expect(await proofs.isProofRequired(id)).to.be.false }) }) - - describe("extend proof end", function () { - const proof = hexlify(randomBytes(42)) - - beforeEach(async function () { - await proofs.expectProofs(id, endId, probability, duration) - }) - - async function waitUntilProofIsRequired(id) { - await advanceTimeTo(periodEnd(periodOf(await currentTime()))) - while ( - !( - (await proofs.isProofRequired(id)) && - (await proofs.getPointer(id)) < 250 - ) - ) { - await advanceTime(period) - } - } - - async function isProofRequiredBefore(id, ending) { - let start = periodOf(await currentTime()) - let end = periodOf(ending) - let periods = end - start - await advanceTimeTo(periodEnd(periodOf(await currentTime()))) - for (let i = 0; i < periods; i++) { - if (await proofs.isProofRequired(id)) { - return true - } - await advanceTime(period) - } - return false - } - - it("can't extend if proof doesn't exist", async function () { - let ending = (await currentTime()) + duration - const otherId = hexlify(randomBytes(32)) - await expect( - proofs.extendProofEndTo(otherId, ending + 1) - ).to.be.revertedWith("endId for given id doesn't exist") - }) - - it("can't extend already lapsed proof ending", async function () { - let ending = (await currentTime()) + duration - await waitUntilProofIsRequired(id) - await advanceTimeTo(ending + 1) - await expect(proofs.extendProofEndTo(id, ending + 1)).to.be.revertedWith( - "Proof already ended" - ) - }) - - it("requires no proofs when ending has not been extended", async function () { - let ending = (await currentTime()) + duration - await expect(await isProofRequiredBefore(id, ending)).to.be.true - let endingExtended = ending + hours(1) - await advanceTimeTo(periodEnd(periodOf(endingExtended) + 1)) - await expect(await isProofRequiredBefore(id, endingExtended)).to.be.false - }) - - it("requires proofs when ending has been extended", async function () { - let ending = (await currentTime()) + duration - await expect(await isProofRequiredBefore(id, ending)).to.be.true - let endingExtended = ending + hours(1) - await proofs.extendProofEndTo(id, endingExtended) - await expect(await isProofRequiredBefore(id, endingExtended)).to.be.true - }) - - it("no longer requires proofs after extension lapsed", async function () { - async function expectNoProofsForPeriods(id, periods) { - await advanceTimeTo(periodEnd(periodOf(await currentTime()))) - for (let i = 0; i < periods; i++) { - await expect(await proofs.isProofRequired(id)).to.be.false - await advanceTime(period) - } - } - - let ending = (await currentTime()) + duration - let endingExtended = ending + hours(1) - await proofs.extendProofEndTo(id, endingExtended) - await advanceTimeTo(periodEnd(periodOf(endingExtended) + 1)) - await expectNoProofsForPeriods(id, 100) - }) - }) }) diff --git a/test/Storage.test.js b/test/Storage.test.js index 1bffc20..4d2c39e 100644 --- a/test/Storage.test.js +++ b/test/Storage.test.js @@ -76,7 +76,7 @@ describe("Storage", function () { describe("ending the contract", function () { it("unlocks the host collateral", async function () { await storage.fillSlot(slot.request, slot.index, proof) - await waitUntilFinished(storage, slotId(slot)) + await waitUntilFinished(storage, slot) await expect(storage.withdraw()).not.to.be.reverted }) }) @@ -121,7 +121,7 @@ describe("Storage", function () { it("frees slot when collateral slashed below minimum threshold", async function () { const id = slotId(slot) - await waitUntilStarted(storage, request.ask.slots, slot.request, proof) + await waitUntilStarted(storage, request, slot, proof) // max slashes before dropping below collateral threshold const maxSlashes = 10 @@ -172,7 +172,7 @@ describe("Storage", function () { it("fails to mark proof as missing when cancelled", async function () { await storage.fillSlot(slot.request, slot.index, proof) - await waitUntilCancelled(request.expiry) + await waitUntilCancelled(request) let missedPeriod = periodOf(await currentTime()) await expect( storage.markProofAsMissing(slotId(slot), missedPeriod) diff --git a/test/marketplace.js b/test/marketplace.js index 050d0ef..94317bb 100644 --- a/test/marketplace.js +++ b/test/marketplace.js @@ -1,24 +1,26 @@ const { advanceTimeTo } = require("./evm") const { slotId } = require("./ids") -async function waitUntilCancelled(expiry) { - await advanceTimeTo(expiry + 1) +async function waitUntilCancelled(request) { + await advanceTimeTo(request.expiry + 1) } -async function waitUntilStarted(contract, numSlots, requestId, proof) { - const lastSlot = numSlots - 1 - for (let i = 0; i <= lastSlot; i++) { - await contract.fillSlot(requestId, i, proof) +async function waitUntilStarted(contract, request, slot, proof) { + const lastSlotIdx = request.ask.slots - 1 + for (let i = 0; i <= lastSlotIdx; i++) { + await contract.fillSlot(slot.request, i, proof) } + return { ...slot, index: lastSlotIdx } } -async function waitUntilFinished(contract, slotId) { - const end = (await contract.proofEnd(slotId)).toNumber() +async function waitUntilFinished(contract, lastSlot) { + const lastSlotId = slotId(lastSlot) + const end = (await contract.proofEnd(lastSlotId)).toNumber() await advanceTimeTo(end + 1) } -async function waitUntilFailed(contract, slot, maxSlotLoss) { - for (let i = 0; i <= maxSlotLoss; i++) { +async function waitUntilFailed(contract, request, slot) { + for (let i = 0; i <= request.ask.maxSlotLoss; i++) { slot.index = i let id = slotId(slot) await contract.freeSlot(id)