[marketplace] add isFinished
Add function `isFinished` that checks whethere the state has been set to `RequestState.Finished` (which is not being set yet) or if the contract was started (`RequestState.Started`) and the contract duration has lapsed. To facilitate `isFinished`, the contract start time (`startedAt`) was added to calculate the contract end time.
This commit is contained in:
parent
2170d6bd19
commit
1fff2f7295
|
@ -104,6 +104,7 @@ contract Marketplace is Collateral, Proofs {
|
|||
emit SlotFilled(requestId, slotIndex, slotId);
|
||||
if (context.slotsFilled == request.ask.slots) {
|
||||
context.state = RequestState.Started;
|
||||
context.startedAt = block.timestamp;
|
||||
_extendLockExpiry(requestId, block.timestamp + request.ask.duration);
|
||||
emit RequestFulfilled(requestId);
|
||||
}
|
||||
|
@ -146,10 +147,9 @@ contract Marketplace is Collateral, Proofs {
|
|||
public
|
||||
marketplaceInvariant
|
||||
{
|
||||
require(_isFinished(requestId), "Contract not ended");
|
||||
bytes32 slotId = keccak256(abi.encode(requestId, slotIndex));
|
||||
require(block.timestamp > proofEnd(slotId), "Contract not ended");
|
||||
Slot storage slot = slots[slotId];
|
||||
require(slot.host != address(0), "Slot empty");
|
||||
Slot storage slot = _slot(slotId);
|
||||
require(!slot.hostPaid, "Already paid");
|
||||
uint256 amount = pricePerSlot(requests[requestId]);
|
||||
funds.sent += amount;
|
||||
|
@ -196,6 +196,21 @@ contract Marketplace is Collateral, Proofs {
|
|||
);
|
||||
}
|
||||
|
||||
/// @notice Return true if the request state is RequestState.Finished or if the request duration has elapsed and the request was started.
|
||||
/// @dev Handles the case when a request may have been finished, but the state has not yet been updated by a transaction.
|
||||
/// @param requestId the id of the request
|
||||
/// @return true if request is finished
|
||||
function _isFinished(bytes32 requestId) internal view returns (bool) {
|
||||
RequestContext memory context = requestContexts[requestId];
|
||||
Request memory request = _request(requestId);
|
||||
return
|
||||
context.state == RequestState.Finished ||
|
||||
(
|
||||
context.state == RequestState.Started &&
|
||||
block.timestamp > context.startedAt + request.ask.duration
|
||||
);
|
||||
}
|
||||
|
||||
/// @notice Return id of request that slot belongs to
|
||||
/// @dev Returns requestId that is mapped to the slotId
|
||||
/// @param slotId id of the slot
|
||||
|
@ -342,6 +357,7 @@ contract Marketplace is Collateral, Proofs {
|
|||
struct RequestContext {
|
||||
uint256 slotsFilled;
|
||||
RequestState state;
|
||||
uint256 startedAt;
|
||||
}
|
||||
|
||||
struct Slot {
|
||||
|
|
|
@ -34,7 +34,7 @@ contract Proofs {
|
|||
return timeout;
|
||||
}
|
||||
|
||||
function _end(bytes32 id) internal view returns (uint256) {
|
||||
function _end(bytes32 id) internal view returns (uint256) {
|
||||
return ends[id];
|
||||
}
|
||||
|
||||
|
|
|
@ -324,7 +324,12 @@ describe("Marketplace", function () {
|
|||
}
|
||||
|
||||
it("pays the host", async function () {
|
||||
await marketplace.fillSlot(slot.request, slot.index, proof)
|
||||
await waitUntilAllSlotsFilled(
|
||||
marketplace,
|
||||
request.ask.slots,
|
||||
slot.request,
|
||||
proof
|
||||
)
|
||||
await waitUntilEnd()
|
||||
const startBalance = await token.balanceOf(host.address)
|
||||
await marketplace.payoutSlot(slot.request, slot.index)
|
||||
|
@ -332,10 +337,10 @@ describe("Marketplace", function () {
|
|||
expect(endBalance - startBalance).to.equal(pricePerSlot(request))
|
||||
})
|
||||
|
||||
it("is only allowed when the slot is filled", async function () {
|
||||
it("is only allowed when the contract is finished", async function () {
|
||||
await expect(
|
||||
marketplace.payoutSlot(slot.request, slot.index)
|
||||
).to.be.revertedWith("Slot empty")
|
||||
).to.be.revertedWith("Contract not ended")
|
||||
})
|
||||
|
||||
it("is only allowed when the contract has ended", async function () {
|
||||
|
@ -346,7 +351,12 @@ describe("Marketplace", function () {
|
|||
})
|
||||
|
||||
it("can only be done once", async function () {
|
||||
await marketplace.fillSlot(slot.request, slot.index, proof)
|
||||
await waitUntilAllSlotsFilled(
|
||||
marketplace,
|
||||
request.ask.slots,
|
||||
slot.request,
|
||||
proof
|
||||
)
|
||||
await waitUntilEnd()
|
||||
await marketplace.payoutSlot(slot.request, slot.index)
|
||||
await expect(
|
||||
|
@ -355,7 +365,12 @@ describe("Marketplace", function () {
|
|||
})
|
||||
|
||||
it("cannot be filled again", async function () {
|
||||
await marketplace.fillSlot(slot.request, slot.index, proof)
|
||||
await waitUntilAllSlotsFilled(
|
||||
marketplace,
|
||||
request.ask.slots,
|
||||
slot.request,
|
||||
proof
|
||||
)
|
||||
await waitUntilEnd()
|
||||
await marketplace.payoutSlot(slot.request, slot.index)
|
||||
await expect(marketplace.fillSlot(slot.request, slot.index, proof)).to.be
|
||||
|
|
|
@ -193,8 +193,7 @@ describe("Storage", function () {
|
|||
await advanceTimeTo(request.expiry + 1)
|
||||
await expect(await storage.willProofBeRequired(id)).to.be.false
|
||||
})
|
||||
|
||||
|
||||
|
||||
|
||||
it("does not require proofs once cancelled", async function () {
|
||||
const id = slotId(slot)
|
||||
|
@ -286,8 +285,40 @@ describe("Storage", function () {
|
|||
await markProofAsMissing(id, onMarkAsMissing)
|
||||
await expect(storage.getSlot(id)).to.be.revertedWith("Slot empty")
|
||||
})
|
||||
})
|
||||
})
|
||||
describe("contract state", function () {
|
||||
it("isCancelled is true once request is cancelled", async function () {
|
||||
await expect(await storage.isCancelled(slot.request)).to.equal(false)
|
||||
await waitUntilExpired(request.expiry)
|
||||
await expect(await storage.isCancelled(slot.request)).to.equal(true)
|
||||
})
|
||||
|
||||
it("isSlotCancelled fails when slot is empty", async function () {
|
||||
await expect(storage.isSlotCancelled(slotId(slot))).to.be.revertedWith(
|
||||
"Slot empty"
|
||||
)
|
||||
})
|
||||
|
||||
it("isSlotCancelled is true once request is cancelled", async function () {
|
||||
await storage.fillSlot(slot.request, slot.index, proof)
|
||||
await waitUntilExpired(request.expiry)
|
||||
await expect(await storage.isSlotCancelled(slotId(slot))).to.equal(true)
|
||||
})
|
||||
|
||||
it("isFinished is true once started and contract duration lapses", async function () {
|
||||
await expect(await storage.isFinished(slot.request)).to.be.false
|
||||
// fill all slots, should change state to RequestState.Started
|
||||
await waitUntilAllSlotsFilled(
|
||||
storage,
|
||||
request.ask.slots,
|
||||
slot.request,
|
||||
proof
|
||||
)
|
||||
await expect(await storage.isFinished(slot.request)).to.be.false
|
||||
advanceTime(request.ask.duration + 1)
|
||||
await expect(await storage.isFinished(slot.request)).to.be.true
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
// TODO: implement checking of actual proofs of storage, instead of dummy bool
|
||||
|
|
Loading…
Reference in New Issue