From b62eeb564aae27a11b25dc7ef136552e7f5cceac Mon Sep 17 00:00:00 2001 From: Mark Spanbroek Date: Wed, 18 Jan 2023 15:26:21 +0100 Subject: [PATCH] [marketplace] test & fix slotState() --- contracts/Marketplace.sol | 27 ++++++------ contracts/Proofs.sol | 2 +- contracts/Requests.sol | 4 +- contracts/TestMarketplace.sol | 7 ++- contracts/TestProofs.sol | 2 +- test/Marketplace.test.js | 82 ++++++++++++++++++++++++++++++++++- test/requests.js | 2 + 7 files changed, 104 insertions(+), 22 deletions(-) diff --git a/contracts/Marketplace.sol b/contracts/Marketplace.sol index 4f5fa25..89601b6 100644 --- a/contracts/Marketplace.sol +++ b/contracts/Marketplace.sol @@ -247,24 +247,25 @@ contract Marketplace is Collateral, Proofs, StateRetrieval { } } - function slotState(SlotId slotId) internal view override returns (SlotState) { + function slotState(SlotId slotId) public view override returns (SlotState) { Slot storage slot = slots[slotId]; + if (RequestId.unwrap(slot.requestId) == 0) { + return SlotState.Free; + } RequestState reqState = requestState(slot.requestId); if (slot.state == SlotState.Paid) { return SlotState.Paid; - } else if ( - slot.state == SlotState.Failed || reqState == RequestState.Failed - ) { - return SlotState.Failed; - } else if ( - slot.state == SlotState.Finished || - reqState == RequestState.Finished || - reqState == RequestState.Cancelled - ) { - return SlotState.Finished; - } else { - return slot.state; } + if (reqState == RequestState.Cancelled) { + return SlotState.Finished; + } + if (reqState == RequestState.Finished) { + return SlotState.Finished; + } + if (reqState == RequestState.Failed) { + return SlotState.Failed; + } + return slot.state; } struct RequestContext { diff --git a/contracts/Proofs.sol b/contracts/Proofs.sol index cda0ef7..8f7339e 100644 --- a/contracts/Proofs.sol +++ b/contracts/Proofs.sol @@ -19,7 +19,7 @@ abstract contract Proofs is Periods { mapping(SlotId => mapping(Period => bool)) private received; mapping(SlotId => mapping(Period => bool)) private missing; - function slotState(SlotId id) internal view virtual returns (SlotState); + function slotState(SlotId id) public view virtual returns (SlotState); function missingProofs(SlotId slotId) public view returns (uint256) { return missed[slotId]; diff --git a/contracts/Requests.sol b/contracts/Requests.sol index 4349218..34f5a67 100644 --- a/contracts/Requests.sol +++ b/contracts/Requests.sol @@ -46,10 +46,10 @@ enum RequestState { } enum SlotState { - Free, // [default] not filled yet, or host has freed slot + Free, // [default] not filled yet, or host has vacated the slot Filled, // host has filled slot Finished, // successfully completed - Failed, // host has missed too many proofs + Failed, // the request has failed Paid // host has been paid } diff --git a/contracts/TestMarketplace.sol b/contracts/TestMarketplace.sol index 99eabf9..31dcadb 100644 --- a/contracts/TestMarketplace.sol +++ b/contracts/TestMarketplace.sol @@ -8,10 +8,9 @@ contract TestMarketplace is Marketplace { constructor( IERC20 token, MarketplaceConfig memory config - ) Marketplace(token, config) // solhint-disable-next-line no-empty-blocks - { - - } + ) + Marketplace(token, config) // solhint-disable-next-line no-empty-blocks + {} function forciblyFreeSlot(SlotId slotId) public { _forciblyFreeSlot(slotId); diff --git a/contracts/TestProofs.sol b/contracts/TestProofs.sol index 6a59bf3..3d2645a 100644 --- a/contracts/TestProofs.sol +++ b/contracts/TestProofs.sol @@ -10,7 +10,7 @@ contract TestProofs is Proofs { // solhint-disable-next-line no-empty-blocks constructor(ProofConfig memory config) Proofs(config) {} - function slotState(SlotId slotId) internal view override returns (SlotState) { + function slotState(SlotId slotId) public view override returns (SlotState) { return states[slotId]; } diff --git a/test/Marketplace.test.js b/test/Marketplace.test.js index 411bbf7..4f2c560 100644 --- a/test/Marketplace.test.js +++ b/test/Marketplace.test.js @@ -6,7 +6,7 @@ const { expect } = require("chai") const { exampleConfiguration, exampleRequest } = require("./examples") const { periodic, hours } = require("./time") const { requestId, slotId, askToArray } = require("./ids") -const { RequestState } = require("./requests") +const { RequestState, SlotState } = require("./requests") const { waitUntilCancelled, waitUntilStarted, @@ -548,6 +548,86 @@ describe("Marketplace", function () { }) }) + describe("slot state", function () { + const { Free, Filled, Finished, Failed, Paid } = SlotState + let period, periodEnd + + beforeEach(async function () { + period = config.proofs.period + ;({ periodOf, periodEnd } = periodic(period)) + + switchAccount(client) + await token.approve(marketplace.address, price(request)) + await marketplace.requestStorage(request) + switchAccount(host) + await token.approve(marketplace.address, config.collateral.initialAmount) + await marketplace.deposit(config.collateral.initialAmount) + }) + + async function waitUntilProofIsRequired(id) { + await advanceTimeTo(periodEnd(periodOf(await currentTime()))) + while ( + !( + (await marketplace.isProofRequired(id)) && + (await marketplace.getPointer(id)) < 250 + ) + ) { + await advanceTime(period) + } + } + + it("is 'Free' initially", async function () { + expect(await marketplace.slotState(slotId(slot))).to.equal(Free) + }) + + it("changes to 'Filled' when slot is filled", async function () { + await marketplace.fillSlot(slot.request, slot.index, proof) + expect(await marketplace.slotState(slotId(slot))).to.equal(Filled) + }) + + it("changes to 'Finished' when request finishes", async function () { + await waitUntilStarted(marketplace, request, proof) + await waitUntilFinished(marketplace, slot.request) + expect(await marketplace.slotState(slotId(slot))).to.equal(Finished) + }) + + it("changes to 'Finished' when request is cancelled", async function () { + await marketplace.fillSlot(slot.request, slot.index, proof) + await waitUntilCancelled(request) + expect(await marketplace.slotState(slotId(slot))).to.equal(Finished) + }) + + it("changes to 'Free' when host frees the slot", async function () { + await marketplace.fillSlot(slot.request, slot.index, proof) + await marketplace.freeSlot(slotId(slot)) + expect(await marketplace.slotState(slotId(slot))).to.equal(Free) + }) + + it("changes to 'Free' when too many proofs are missed", async function () { + await waitUntilStarted(marketplace, request, proof) + while ((await marketplace.slotState(slotId(slot))) === Filled) { + await waitUntilProofIsRequired(slotId(slot)) + const missedPeriod = periodOf(await currentTime()) + await advanceTime(period) + await marketplace.markProofAsMissing(slotId(slot), missedPeriod) + } + expect(await marketplace.slotState(slotId(slot))).to.equal(Free) + }) + + it("changes to 'Failed' when request fails", async function () { + await waitUntilStarted(marketplace, request, proof) + await waitUntilSlotFailed(marketplace, request, slot) + expect(await marketplace.slotState(slotId(slot))).to.equal(Failed) + }) + + it("changes to 'Paid' when host has been paid", async function () { + await waitUntilStarted(marketplace, request, proof) + await waitUntilFinished(marketplace, slot.request) + await marketplace.freeSlot(slotId(slot)) + expect(await marketplace.slotState(slotId(slot))).to.equal(Paid) + }) + }) + describe("proof requirements", function () { let period, periodOf, periodEnd diff --git a/test/requests.js b/test/requests.js index 2a5ade8..af30b32 100644 --- a/test/requests.js +++ b/test/requests.js @@ -10,6 +10,8 @@ const SlotState = { Free: 0, Filled: 1, Finished: 2, + Failed: 3, + Paid: 4, } module.exports = { RequestState, SlotState }