marketplace: allow slot payout for failed requests

reason: hosts are paid for the time that they hosted
the slot up until the time that the request failed

Co-Authored-By: Adam Uhlíř <adam@uhlir.dev>
This commit is contained in:
Mark Spanbroek 2025-05-27 11:02:11 +02:00
parent 2cd45214cf
commit 724344cb5e
2 changed files with 48 additions and 3 deletions

View File

@ -263,11 +263,10 @@ contract Marketplace is SlotReservations, Proofs, StateRetrieval, Endian {
SlotState state = slotState(slotId);
if (
state == SlotState.Finished ||
state == SlotState.Cancelled
state == SlotState.Cancelled ||
state == SlotState.Failed
) {
_payoutSlot(slot.requestId, slotId);
} else if (state == SlotState.Failed) {
_removeFromMySlots(msg.sender, slotId);
} else if (state == SlotState.Filled) {
// free slot without returning collateral, effectively a 100% slash
_forciblyFreeSlot(slotId);

View File

@ -650,6 +650,27 @@ describe("Marketplace", function () {
expect(endBalance - startBalance).to.be.equal(expectedPartialPayout)
})
it("pays the host when contract fails and then finishes", async function () {
await advanceTime(10)
await waitUntilStarted(marketplace, request, proof, token)
const filledAt = await currentTime()
await advanceTime(10)
await waitUntilSlotFailed(marketplace, request, slot)
const failedAt = await currentTime()
await advanceTime(10)
await waitUntilFinished(marketplace, requestId(request))
const startBalance = await token.balanceOf(host.address)
await marketplace.freeSlot(slotId(slot))
const endBalance = await token.balanceOf(host.address)
const payout = (failedAt - filledAt) * pricePerSlotPerSecond(request)
const collateral = collateralPerSlot(request)
expect(endBalance - startBalance).to.equal(payout + collateral)
})
it("updates the collateral when freeing a finished slot", async function () {
await waitUntilStarted(marketplace, request, proof, token)
await waitUntilFinished(marketplace, requestId(request))
@ -665,6 +686,14 @@ describe("Marketplace", function () {
expect(await marketplace.currentCollateral(slotId(slot))).to.equal(0)
})
it("updates the collateral when freeing a failed slot", async function () {
await waitUntilStarted(marketplace, request, proof, token)
await waitUntilSlotFailed(marketplace, request, slot)
await waitUntilFinished(marketplace, requestId(request))
await marketplace.freeSlot(slotId(slot))
expect(await marketplace.currentCollateral(slotId(slot))).to.equal(0)
})
it("does not pay when the contract hasn't ended", async function () {
await marketplace.reserveSlot(slot.request, slot.index)
await marketplace.fillSlot(slot.request, slot.index, proof)
@ -674,6 +703,14 @@ describe("Marketplace", function () {
expect(endBalance).to.equal(startBalance)
})
it("does not pay for a failed slot when the contract hasn't ended", async function () {
await waitUntilStarted(marketplace, request, proof, token)
await waitUntilSlotFailed(marketplace, request, slot)
await expect(marketplace.freeSlot(slotId(slot))).to.be.revertedWith(
"VaultFundNotUnlocked"
)
})
it("pays only once", async function () {
await waitUntilStarted(marketplace, request, proof, token)
await waitUntilFinished(marketplace, requestId(request))
@ -798,6 +835,14 @@ describe("Marketplace", function () {
)
})
it("rejects withdraw for failed request before request end", async function () {
await waitUntilStarted(marketplace, request, proof, token)
await waitUntilFailed(marketplace, request)
await expect(marketplace.withdrawFunds(slot.request)).to.be.revertedWith(
"VaultFundNotUnlocked"
)
})
it("does not withdraw more than once", async function () {
await waitUntilStarted(marketplace, request, proof, token)
await waitUntilFinished(marketplace, requestId(request))
@ -1387,6 +1432,7 @@ describe("Marketplace", function () {
it("removes slot when failed slot is freed", async function () {
await waitUntilStarted(marketplace, request, proof, token)
await waitUntilSlotFailed(marketplace, request, slot)
await waitUntilFinished(marketplace, requestId(request))
await marketplace.freeSlot(slotId(slot))
expect(await marketplace.mySlots()).to.not.contain(slotId(slot))
})