diff --git a/contracts/Marketplace.sol b/contracts/Marketplace.sol index f2786db..1ee31fd 100644 --- a/contracts/Marketplace.sol +++ b/contracts/Marketplace.sol @@ -55,10 +55,12 @@ contract Marketplace is Collateral, Proofs { RequestContext storage context = requestContexts[requestId]; require(context.state == RequestState.Started, "Invalid state"); - _removeAccountLock(slot.host, requestId); - // TODO: burn host's collateral except for repair costs + mark proof + + // TODO: burn host's slot collateral except for repair costs + mark proof // missing reward + // Slot collateral is not yet implemented as the design decision was + // not finalised. _unexpectProofs(slotId); @@ -66,6 +68,17 @@ contract Marketplace is Collateral, Proofs { slot.requestId = 0; context.slotsFilled -= 1; emit SlotFreed(requestId, slotId); + + Request memory request = _request(requestId); + uint256 slotsLost = request.ask.slots - context.slotsFilled; + if (slotsLost > request.ask.maxSlotLoss) { + context.state = RequestState.Failed; + emit RequestFailed(requestId); + + // TODO: burn all remaining slot collateral (note: slot collateral not + // yet implemented) + // TODO: send client remaining funds + } } function fillSlot( @@ -74,10 +87,10 @@ contract Marketplace is Collateral, Proofs { bytes calldata proof ) public marketplaceInvariant { Request storage request = _request(requestId); - // TODO: change below to check !_isCancelled(requestId) instead? - require(request.expiry > block.timestamp, "Request expired"); require(slotIndex < request.ask.slots, "Invalid slot"); RequestContext storage context = requestContexts[requestId]; + // TODO: change below to check !_isCancelled(requestId) instead? + require(!_isCancelled(requestId), "Request cancelled"); // TODO: in the case of repair, update below require condition by adding // || context.state == RequestState.Started require(context.state == RequestState.New, "Invalid state"); @@ -273,6 +286,7 @@ contract Marketplace is Collateral, Proofs { uint256 duration; // how long content should be stored (in seconds) uint256 proofProbability; // how often storage proofs are required uint256 reward; // amount of tokens paid per second per slot to hosts + uint64 maxSlotLoss; // Max slots that can be lost without data considered to be lost } struct Content { @@ -312,6 +326,7 @@ contract Marketplace is Collateral, Proofs { event StorageRequested(bytes32 requestId, Ask ask); event RequestFulfilled(bytes32 indexed requestId); + event RequestFailed(bytes32 indexed requestId); event SlotFilled( bytes32 indexed requestId, uint256 indexed slotIndex, diff --git a/contracts/Proofs.sol b/contracts/Proofs.sol index 822c1d2..0bd0f12 100644 --- a/contracts/Proofs.sol +++ b/contracts/Proofs.sol @@ -118,7 +118,6 @@ contract Proofs { 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; } diff --git a/test/Marketplace.test.js b/test/Marketplace.test.js index 9d58a47..08d853e 100644 --- a/test/Marketplace.test.js +++ b/test/Marketplace.test.js @@ -215,7 +215,7 @@ describe("Marketplace", function () { ).to.be.revertedWith("Unknown request") }) - it("is rejected when request is expired", async function () { + it("is rejected when request is expired/cancelled", async function () { switchAccount(client) let expired = { ...request, expiry: now() - hours(1) } await token.approve(marketplace.address, price(request)) @@ -223,7 +223,7 @@ describe("Marketplace", function () { switchAccount(host) await expect( marketplace.fillSlot(requestId(expired), slot.index, proof) - ).to.be.revertedWith("Request expired") + ).to.be.revertedWith("Request cancelled") }) it("is rejected when slot index not in range", async function () { diff --git a/test/examples.js b/test/examples.js index b6302a8..b4ab51c 100644 --- a/test/examples.js +++ b/test/examples.js @@ -10,6 +10,7 @@ const exampleRequest = () => ({ duration: hours(10), proofProbability: 4, // require a proof roughly once every 4 periods reward: 84, + maxSlotLoss: 2, }, content: { cid: "zb2rhheVmk3bLks5MgzTqyznLu1zqGH5jrfTA1eAZXrjx7Vob", diff --git a/test/ids.js b/test/ids.js index 6f9ca95..2937a0e 100644 --- a/test/ids.js +++ b/test/ids.js @@ -2,7 +2,7 @@ const { ethers } = require("hardhat") const { keccak256, defaultAbiCoder } = ethers.utils function requestId(request) { - const Ask = "tuple(int64, uint256, uint256, uint256, uint256)" + const Ask = "tuple(int64, uint256, uint256, uint256, uint256, int64)" const Erasure = "tuple(uint64)" const PoR = "tuple(bytes, bytes, bytes)" const Content = "tuple(string, " + Erasure + ", " + PoR + ")" @@ -18,6 +18,7 @@ function askToArray(ask) { ask.duration, ask.proofProbability, ask.reward, + ask.maxSlotLoss, ] }