[marketplace] introduce SlotState
- replace proofEnd() by slotState() - replace _slotAcceptsProofs() by slotState() - remove _stopRequiringProofs()
This commit is contained in:
parent
de0595edb3
commit
c8dda37300
|
@ -80,7 +80,9 @@ contract Marketplace is Collateral, Proofs, StateRetrieval {
|
|||
|
||||
SlotId slotId = Requests.slotId(requestId, slotIndex);
|
||||
Slot storage slot = slots[slotId];
|
||||
require(slot.host == address(0), "Slot already filled");
|
||||
slot.requestId = requestId;
|
||||
|
||||
require(slotState(slotId) == SlotState.Free, "Slot already filled");
|
||||
|
||||
require(balanceOf(msg.sender) >= collateral, "Insufficient collateral");
|
||||
|
||||
|
@ -88,7 +90,7 @@ contract Marketplace is Collateral, Proofs, StateRetrieval {
|
|||
submitProof(slotId, proof);
|
||||
|
||||
slot.host = msg.sender;
|
||||
slot.requestId = requestId;
|
||||
slot.state = SlotState.Filled;
|
||||
RequestContext storage context = _context(requestId);
|
||||
context.slotsFilled += 1;
|
||||
|
||||
|
@ -150,10 +152,9 @@ contract Marketplace is Collateral, Proofs, StateRetrieval {
|
|||
// Slot collateral is not yet implemented as the design decision was
|
||||
// not finalised.
|
||||
|
||||
_stopRequiringProofs(slotId);
|
||||
|
||||
removeFromMySlots(slot.host, slotId);
|
||||
|
||||
slot.state = SlotState.Free;
|
||||
slot.host = address(0);
|
||||
slot.requestId = RequestId.wrap(0);
|
||||
context.slotsFilled -= 1;
|
||||
|
@ -258,7 +259,7 @@ contract Marketplace is Collateral, Proofs, StateRetrieval {
|
|||
|
||||
function _slot(SlotId slotId) internal view returns (Slot storage) {
|
||||
Slot storage slot = slots[slotId];
|
||||
require(slot.host != address(0), "Slot empty");
|
||||
require(slot.state == SlotState.Filled, "Slot empty");
|
||||
return slot;
|
||||
}
|
||||
|
||||
|
@ -272,10 +273,6 @@ contract Marketplace is Collateral, Proofs, StateRetrieval {
|
|||
return secondsPerPeriod;
|
||||
}
|
||||
|
||||
function proofEnd(SlotId slotId) public view override returns (uint256) {
|
||||
return requestEnd(_slot(slotId).requestId);
|
||||
}
|
||||
|
||||
function requestEnd(RequestId requestId) public view returns (uint256) {
|
||||
uint256 end = _context(requestId).endsAt;
|
||||
if (_requestAcceptsProofs(requestId)) {
|
||||
|
@ -286,21 +283,21 @@ contract Marketplace is Collateral, Proofs, StateRetrieval {
|
|||
}
|
||||
|
||||
function isProofRequired(SlotId slotId) public view returns (bool) {
|
||||
if (!_slotAcceptsProofs(slotId)) {
|
||||
if (slotState(slotId) != SlotState.Filled) {
|
||||
return false;
|
||||
}
|
||||
return _isProofRequired(slotId);
|
||||
}
|
||||
|
||||
function willProofBeRequired(SlotId slotId) public view returns (bool) {
|
||||
if (!_slotAcceptsProofs(slotId)) {
|
||||
if (slotState(slotId) != SlotState.Filled) {
|
||||
return false;
|
||||
}
|
||||
return _willProofBeRequired(slotId);
|
||||
}
|
||||
|
||||
function getChallenge(SlotId slotId) public view returns (bytes32) {
|
||||
if (!_slotAcceptsProofs(slotId)) {
|
||||
if (slotState(slotId) != SlotState.Filled) {
|
||||
return bytes32(0);
|
||||
}
|
||||
return _getChallenge(slotId);
|
||||
|
@ -348,12 +345,18 @@ contract Marketplace is Collateral, Proofs, StateRetrieval {
|
|||
}
|
||||
}
|
||||
|
||||
/// @notice returns true when the request is accepting proof submissions from hosts occupying slots.
|
||||
/// @dev Request state must be new or started, and must not be cancelled, finished, or failed.
|
||||
/// @param slotId id of the slot, that is mapped to a request, for which to obtain state info
|
||||
function _slotAcceptsProofs(SlotId slotId) internal view returns (bool) {
|
||||
RequestId requestId = _getRequestIdForSlot(slotId);
|
||||
return _requestAcceptsProofs(requestId);
|
||||
function slotState(SlotId slotId) internal view override returns (SlotState) {
|
||||
Slot storage slot = slots[slotId];
|
||||
RequestState reqState = requestState(slot.requestId);
|
||||
if (
|
||||
reqState == RequestState.Finished ||
|
||||
reqState == RequestState.Cancelled ||
|
||||
reqState == RequestState.Failed
|
||||
) {
|
||||
return SlotState.Finished;
|
||||
} else {
|
||||
return slot.state;
|
||||
}
|
||||
}
|
||||
|
||||
/// @notice returns true when the request is accepting proof submissions from hosts occupying slots.
|
||||
|
@ -367,13 +370,14 @@ contract Marketplace is Collateral, Proofs, StateRetrieval {
|
|||
}
|
||||
|
||||
struct RequestContext {
|
||||
uint256 slotsFilled;
|
||||
RequestState state;
|
||||
uint256 slotsFilled;
|
||||
uint256 startedAt;
|
||||
uint256 endsAt;
|
||||
}
|
||||
|
||||
struct Slot {
|
||||
SlotState state;
|
||||
address host;
|
||||
bool hostPaid;
|
||||
RequestId requestId;
|
||||
|
|
|
@ -18,16 +18,13 @@ abstract contract Proofs is Periods {
|
|||
downtime = __downtime;
|
||||
}
|
||||
|
||||
mapping(SlotId => bool) private slotIds;
|
||||
mapping(SlotId => uint256) private slotStarts;
|
||||
mapping(SlotId => uint256) private probabilities;
|
||||
mapping(SlotId => uint256) private missed;
|
||||
mapping(SlotId => mapping(Period => bool)) private received;
|
||||
mapping(SlotId => mapping(Period => bool)) private missing;
|
||||
|
||||
// Override this to let the proving system know when proofs for a
|
||||
// slot are no longer required.
|
||||
function proofEnd(SlotId id) public view virtual returns (uint256);
|
||||
function slotState(SlotId id) internal view virtual returns (SlotState);
|
||||
|
||||
function missingProofs(SlotId slotId) public view returns (uint256) {
|
||||
return missed[slotId];
|
||||
|
@ -37,17 +34,10 @@ abstract contract Proofs is Periods {
|
|||
/// @dev Requires that the id is not already in use
|
||||
/// @param probability The probability that a proof should be expected
|
||||
function _startRequiringProofs(SlotId id, uint256 probability) internal {
|
||||
require(!slotIds[id], "Proofs already required for slot");
|
||||
slotIds[id] = true;
|
||||
slotStarts[id] = block.timestamp;
|
||||
probabilities[id] = probability;
|
||||
}
|
||||
|
||||
function _stopRequiringProofs(SlotId id) internal {
|
||||
require(slotIds[id], "Proofs not required for slot");
|
||||
slotIds[id] = false;
|
||||
}
|
||||
|
||||
function _getPointer(
|
||||
SlotId id,
|
||||
Period proofPeriod
|
||||
|
@ -84,15 +74,15 @@ abstract contract Proofs is Periods {
|
|||
SlotId id,
|
||||
Period proofPeriod
|
||||
) internal view returns (bool isRequired, uint8 pointer) {
|
||||
SlotState state = slotState(id);
|
||||
Period start = periodOf(slotStarts[id]);
|
||||
Period end = periodOf(proofEnd(id));
|
||||
if (!isAfter(proofPeriod, start) || !isBefore(proofPeriod, end)) {
|
||||
if (state != SlotState.Filled || !isAfter(proofPeriod, start)) {
|
||||
return (false, 0);
|
||||
}
|
||||
pointer = _getPointer(id, proofPeriod);
|
||||
bytes32 challenge = _getChallenge(pointer);
|
||||
uint256 probability = (probabilities[id] * (256 - downtime)) / 256;
|
||||
isRequired = slotIds[id] && uint256(challenge) % probability == 0;
|
||||
isRequired = uint256(challenge) % probability == 0;
|
||||
}
|
||||
|
||||
function _isProofRequired(
|
||||
|
|
|
@ -45,6 +45,12 @@ enum RequestState {
|
|||
Failed // too many nodes have failed to provide proofs, data lost
|
||||
}
|
||||
|
||||
enum SlotState {
|
||||
Free, // [default] not filled yet, or host has freed slot
|
||||
Filled, // host has filled slot
|
||||
Finished // successfully or unsuccessfully completed
|
||||
}
|
||||
|
||||
library Requests {
|
||||
function id(Request memory request) internal pure returns (RequestId) {
|
||||
return RequestId.wrap(keccak256(abi.encode(request)));
|
||||
|
|
|
@ -5,7 +5,7 @@ import "./Proofs.sol";
|
|||
|
||||
// exposes internal functions of Proofs for testing
|
||||
contract TestProofs is Proofs {
|
||||
mapping(SlotId => uint256) private ends;
|
||||
mapping(SlotId => SlotState) private states;
|
||||
|
||||
constructor(
|
||||
uint256 __period,
|
||||
|
@ -18,18 +18,14 @@ contract TestProofs is Proofs {
|
|||
|
||||
}
|
||||
|
||||
function proofEnd(SlotId slotId) public view override returns (uint256) {
|
||||
return ends[slotId];
|
||||
function slotState(SlotId slotId) internal view override returns (SlotState) {
|
||||
return states[slotId];
|
||||
}
|
||||
|
||||
function startRequiringProofs(SlotId slot, uint256 _probability) public {
|
||||
_startRequiringProofs(slot, _probability);
|
||||
}
|
||||
|
||||
function stopRequiringProofs(SlotId id) public {
|
||||
_stopRequiringProofs(id);
|
||||
}
|
||||
|
||||
function isProofRequired(SlotId id) public view returns (bool) {
|
||||
return _isProofRequired(id);
|
||||
}
|
||||
|
@ -50,7 +46,7 @@ contract TestProofs is Proofs {
|
|||
_markProofAsMissing(id, _period);
|
||||
}
|
||||
|
||||
function setProofEnd(SlotId id, uint256 end) public {
|
||||
ends[id] = end;
|
||||
function setSlotState(SlotId id, SlotState state) public {
|
||||
states[id] = state;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,13 +6,13 @@ const { expect } = require("chai")
|
|||
const { exampleRequest } = require("./examples")
|
||||
const { periodic, hours, minutes } = require("./time")
|
||||
const { requestId, slotId, askToArray } = require("./ids")
|
||||
const { RequestState } = require("./requests")
|
||||
const {
|
||||
waitUntilCancelled,
|
||||
waitUntilStarted,
|
||||
waitUntilFinished,
|
||||
waitUntilFailed,
|
||||
waitUntilSlotFailed,
|
||||
RequestState,
|
||||
} = require("./marketplace")
|
||||
const { price, pricePerSlot } = require("./price")
|
||||
const {
|
||||
|
@ -152,11 +152,6 @@ describe("Marketplace", function () {
|
|||
expect(await marketplace.getHost(slotId(slot))).to.equal(host.address)
|
||||
})
|
||||
|
||||
it("starts requiring storage proofs", async function () {
|
||||
await marketplace.fillSlot(slot.request, slot.index, proof)
|
||||
expect(await marketplace.proofEnd(slotId(slot))).to.be.gt(0)
|
||||
})
|
||||
|
||||
it("is rejected when proof is incorrect", async function () {
|
||||
let invalid = hexlify([])
|
||||
await expect(
|
||||
|
@ -233,66 +228,6 @@ describe("Marketplace", function () {
|
|||
})
|
||||
})
|
||||
|
||||
describe("proof end", function () {
|
||||
var requestTime
|
||||
beforeEach(async function () {
|
||||
switchAccount(client)
|
||||
await token.approve(marketplace.address, price(request))
|
||||
await marketplace.requestStorage(request)
|
||||
requestTime = await currentTime()
|
||||
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++) {
|
||||
await marketplace.fillSlot(slot.request, i, proof)
|
||||
}
|
||||
advanceTime(minutes(10))
|
||||
await marketplace.fillSlot(slot.request, lastSlot, proof)
|
||||
let slot0 = { ...slot, index: 0 }
|
||||
let end = await marketplace.proofEnd(slotId(slot0))
|
||||
for (let i = 1; i <= lastSlot; i++) {
|
||||
let sloti = { ...slot, index: i }
|
||||
await expect((await marketplace.proofEnd(slotId(sloti))) === end)
|
||||
}
|
||||
})
|
||||
|
||||
it("sets the proof end time to now + duration", async function () {
|
||||
await marketplace.fillSlot(slot.request, slot.index, proof)
|
||||
await expect(
|
||||
(await marketplace.proofEnd(slotId(slot))).toNumber()
|
||||
).to.be.closeTo(requestTime + request.ask.duration, 1)
|
||||
})
|
||||
|
||||
it("sets proof end time to the past once failed", async function () {
|
||||
await waitUntilStarted(marketplace, request, proof)
|
||||
await waitUntilFailed(marketplace, request)
|
||||
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("checks that proof end time is in the past once finished", async function () {
|
||||
await waitUntilStarted(marketplace, request, proof)
|
||||
await waitUntilFinished(marketplace, requestId(request))
|
||||
const now = await currentTime()
|
||||
// in the process of calling currentTime and proofEnd,
|
||||
// block.timestamp has advanced by 1, so the expected proof end time will
|
||||
// be block.timestamp - 1.
|
||||
await expect(await marketplace.proofEnd(slotId(slot))).to.be.eq(now - 1)
|
||||
})
|
||||
})
|
||||
|
||||
describe("request end", function () {
|
||||
var requestTime
|
||||
beforeEach(async function () {
|
||||
|
@ -305,21 +240,6 @@ describe("Marketplace", function () {
|
|||
await marketplace.deposit(collateral)
|
||||
})
|
||||
|
||||
it("shares request end time for all slots in request", async function () {
|
||||
const lastSlot = request.ask.slots - 1
|
||||
for (let i = 0; i < lastSlot; i++) {
|
||||
await marketplace.fillSlot(slot.request, i, proof)
|
||||
}
|
||||
advanceTime(minutes(10))
|
||||
await marketplace.fillSlot(slot.request, lastSlot, proof)
|
||||
let slot0 = { ...slot, index: 0 }
|
||||
let end = await marketplace.requestEnd(requestId(request))
|
||||
for (let i = 1; i <= lastSlot; i++) {
|
||||
let sloti = { ...slot, index: i }
|
||||
await expect((await marketplace.proofEnd(slotId(sloti))) === end)
|
||||
}
|
||||
})
|
||||
|
||||
it("sets the request end time to now + duration", async function () {
|
||||
await marketplace.fillSlot(slot.request, slot.index, proof)
|
||||
await expect(
|
||||
|
@ -350,7 +270,7 @@ describe("Marketplace", function () {
|
|||
await waitUntilStarted(marketplace, request, proof)
|
||||
await waitUntilFinished(marketplace, requestId(request))
|
||||
const now = await currentTime()
|
||||
// in the process of calling currentTime and proofEnd,
|
||||
// in the process of calling currentTime and requestEnd,
|
||||
// block.timestamp has advanced by 1, so the expected proof end time will
|
||||
// be block.timestamp - 1.
|
||||
await expect(await marketplace.requestEnd(requestId(request))).to.be.eq(
|
||||
|
@ -652,17 +572,6 @@ describe("Marketplace", function () {
|
|||
RequestState.Cancelled
|
||||
)
|
||||
})
|
||||
|
||||
it("changes proofEnd to the past when request is cancelled", async function () {
|
||||
await marketplace.fillSlot(slot.request, slot.index, proof)
|
||||
await expect(await marketplace.proofEnd(slotId(slot))).to.be.gt(
|
||||
await currentTime()
|
||||
)
|
||||
await waitUntilCancelled(request)
|
||||
await expect(await marketplace.proofEnd(slotId(slot))).to.be.lt(
|
||||
await currentTime()
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("proof requirements", function () {
|
||||
|
@ -698,6 +607,12 @@ describe("Marketplace", function () {
|
|||
}
|
||||
}
|
||||
|
||||
it("requires proofs when slot is filled", async function () {
|
||||
const id = slotId(slot)
|
||||
await marketplace.fillSlot(slot.request, slot.index, proof)
|
||||
await waitUntilProofWillBeRequired(id)
|
||||
})
|
||||
|
||||
it("will not require proofs once cancelled", async function () {
|
||||
const id = slotId(slot)
|
||||
await marketplace.fillSlot(slot.request, slot.index, proof)
|
||||
|
|
|
@ -11,6 +11,7 @@ const {
|
|||
advanceTimeTo,
|
||||
} = require("./evm")
|
||||
const { periodic } = require("./time")
|
||||
const { SlotState } = require("./requests")
|
||||
|
||||
describe("Proofs", function () {
|
||||
const slotId = hexlify(randomBytes(32))
|
||||
|
@ -37,14 +38,7 @@ describe("Proofs", function () {
|
|||
|
||||
describe("general", function () {
|
||||
beforeEach(async function () {
|
||||
await proofs.setProofEnd(slotId, (await currentTime()) + duration)
|
||||
})
|
||||
|
||||
it("does not allow ids to be reused", async function () {
|
||||
await proofs.startRequiringProofs(slotId, probability)
|
||||
await expect(
|
||||
proofs.startRequiringProofs(slotId, probability)
|
||||
).to.be.revertedWith("Proofs already required for slot")
|
||||
await proofs.setSlotState(slotId, SlotState.Filled)
|
||||
})
|
||||
|
||||
it("requires proofs with an agreed upon probability", async function () {
|
||||
|
@ -70,26 +64,12 @@ describe("Proofs", function () {
|
|||
}
|
||||
})
|
||||
|
||||
it("requires no proofs in the end period", async function () {
|
||||
const probability = 1
|
||||
await proofs.startRequiringProofs(slotId, probability)
|
||||
await advanceTime(duration)
|
||||
expect(await proofs.isProofRequired(slotId)).to.be.false
|
||||
})
|
||||
|
||||
it("requires no proofs after the end time", async function () {
|
||||
const probability = 1
|
||||
await proofs.startRequiringProofs(slotId, probability)
|
||||
await advanceTime(duration + timeout)
|
||||
expect(await proofs.isProofRequired(slotId)).to.be.false
|
||||
})
|
||||
|
||||
it("requires proofs for different ids at different times", async function () {
|
||||
let id1 = hexlify(randomBytes(32))
|
||||
let id2 = hexlify(randomBytes(32))
|
||||
let id3 = hexlify(randomBytes(32))
|
||||
for (let slotId of [id1, id2, id3]) {
|
||||
await proofs.setProofEnd(slotId, (await currentTime()) + duration)
|
||||
await proofs.setSlotState(slotId, SlotState.Filled)
|
||||
await proofs.startRequiringProofs(slotId, probability)
|
||||
}
|
||||
let req1, req2, req3
|
||||
|
@ -120,7 +100,7 @@ describe("Proofs", function () {
|
|||
}
|
||||
|
||||
beforeEach(async function () {
|
||||
await proofs.setProofEnd(slotId, (await currentTime()) + duration)
|
||||
await proofs.setSlotState(slotId, SlotState.Filled)
|
||||
await proofs.startRequiringProofs(slotId, probability)
|
||||
await advanceTimeTo(periodEnd(periodOf(await currentTime())))
|
||||
await waitUntilProofWillBeRequired()
|
||||
|
@ -142,10 +122,10 @@ describe("Proofs", function () {
|
|||
expect(await proofs.isProofRequired(slotId)).to.be.true
|
||||
})
|
||||
|
||||
it("will not require proofs when no longer expected", async function () {
|
||||
it("will not require proofs when slot is finished", async function () {
|
||||
expect(await proofs.getPointer(slotId)).to.be.lt(downtime)
|
||||
expect(await proofs.willProofBeRequired(slotId)).to.be.true
|
||||
await proofs.stopRequiringProofs(slotId)
|
||||
await proofs.setSlotState(slotId, SlotState.Finished)
|
||||
expect(await proofs.willProofBeRequired(slotId)).to.be.false
|
||||
})
|
||||
})
|
||||
|
@ -154,7 +134,7 @@ describe("Proofs", function () {
|
|||
const proof = hexlify(randomBytes(42))
|
||||
|
||||
beforeEach(async function () {
|
||||
await proofs.setProofEnd(slotId, (await currentTime()) + duration)
|
||||
await proofs.setSlotState(slotId, SlotState.Filled)
|
||||
await proofs.startRequiringProofs(slotId, probability)
|
||||
})
|
||||
|
||||
|
@ -268,10 +248,10 @@ describe("Proofs", function () {
|
|||
).to.be.revertedWith("Proof already marked as missing")
|
||||
})
|
||||
|
||||
it("requires no proofs when no longer expected", async function () {
|
||||
it("requires no proofs when slot is finished", async function () {
|
||||
await waitUntilProofIsRequired(slotId)
|
||||
await proofs.stopRequiringProofs(slotId)
|
||||
await expect(await proofs.isProofRequired(slotId)).to.be.false
|
||||
await proofs.setSlotState(slotId, SlotState.Finished)
|
||||
expect(await proofs.isProofRequired(slotId)).to.be.false
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -37,19 +37,10 @@ async function waitUntilSlotFailed(contract, request, slot) {
|
|||
}
|
||||
}
|
||||
|
||||
const RequestState = {
|
||||
New: 0,
|
||||
Started: 1,
|
||||
Cancelled: 2,
|
||||
Finished: 3,
|
||||
Failed: 4,
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
waitUntilCancelled,
|
||||
waitUntilStarted,
|
||||
waitUntilFinished,
|
||||
waitUntilFailed,
|
||||
waitUntilSlotFailed,
|
||||
RequestState,
|
||||
}
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
const RequestState = {
|
||||
New: 0,
|
||||
Started: 1,
|
||||
Cancelled: 2,
|
||||
Finished: 3,
|
||||
Failed: 4,
|
||||
}
|
||||
|
||||
const SlotState = {
|
||||
Free: 0,
|
||||
Filled: 1,
|
||||
Finished: 2,
|
||||
}
|
||||
|
||||
module.exports = { RequestState, SlotState }
|
Loading…
Reference in New Issue