[marketplace] Add slots

This commit is contained in:
Mark Spanbroek 2022-07-19 11:33:54 +02:00 committed by markspanbroek
parent 97ef1bcd9f
commit 141abce186
3 changed files with 126 additions and 1 deletions

View File

@ -10,6 +10,7 @@ contract Marketplace is Collateral, Proofs {
MarketplaceFunds private funds;
mapping(bytes32 => Request) private requests;
mapping(bytes32 => RequestState) private requestState;
mapping(bytes32 => Slot) private slots;
constructor(
IERC20 _token,
@ -45,6 +46,30 @@ contract Marketplace is Collateral, Proofs {
emit StorageRequested(id, request.ask);
}
function fillSlot(
bytes32 requestId,
uint256 slotIndex,
bytes calldata proof
) public marketplaceInvariant {
Request storage request = requests[requestId];
require(request.client != address(0), "Unknown request");
require(request.expiry > block.timestamp, "Request expired");
require(slotIndex < request.content.erasure.totalNodes, "Invalid slot");
bytes32 slotId = keccak256(abi.encode(requestId, slotIndex));
Slot storage slot = slots[slotId];
require(slot.host == address(0), "Slot already filled");
require(balanceOf(msg.sender) >= collateral, "Insufficient collateral");
_lock(msg.sender, requestId);
_expectProofs(slotId, request.ask.proofProbability, request.ask.duration);
_submitProof(slotId, proof);
slot.host = msg.sender;
emit SlotFilled(requestId, slotIndex, slotId);
}
function fulfillRequest(bytes32 requestId, bytes calldata proof)
public
marketplaceInvariant
@ -127,8 +152,17 @@ contract Marketplace is Collateral, Proofs {
address host;
}
struct Slot {
address host;
}
event StorageRequested(bytes32 requestId, Ask ask);
event RequestFulfilled(bytes32 indexed requestId);
event SlotFilled(
bytes32 indexed requestId,
uint256 indexed slotIndex,
bytes32 indexed slotId
);
modifier marketplaceInvariant() {
MarketplaceFunds memory oldFunds = funds;

View File

@ -4,7 +4,7 @@ const { expect } = require("chai")
const { exampleRequest } = require("./examples")
const { snapshot, revert, ensureMinimumBlockHeight } = require("./evm")
const { now, hours } = require("./time")
const { requestId, askToArray } = require("./ids")
const { requestId, slotId, askToArray } = require("./ids")
describe("Marketplace", function () {
const collateral = 100
@ -88,6 +88,89 @@ describe("Marketplace", function () {
})
})
describe("filling a slot", function () {
const proof = hexlify(randomBytes(42))
let slot
beforeEach(async function () {
switchAccount(client)
await token.approve(marketplace.address, request.ask.reward)
await marketplace.requestStorage(request)
switchAccount(host)
await token.approve(marketplace.address, collateral)
await marketplace.deposit(collateral)
slot = {
request: requestId(request),
index: request.content.erasure.totalNodes / 2,
}
})
it("emits event when slot is filled", async function () {
await expect(marketplace.fillSlot(slot.request, slot.index, proof))
.to.emit(marketplace, "SlotFilled")
.withArgs(slot.request, slot.index, slotId(slot))
})
it("locks collateral of host", async function () {
await marketplace.fillSlot(slot.request, slot.index, proof)
await expect(marketplace.withdraw()).to.be.revertedWith("Account locked")
})
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(
marketplace.fillSlot(slot.request, slot.index, invalid)
).to.be.revertedWith("Invalid proof")
})
it("is rejected when collateral is insufficient", async function () {
let insufficient = collateral - 1
await marketplace.withdraw()
await token.approve(marketplace.address, insufficient)
await marketplace.deposit(insufficient)
await expect(
marketplace.fillSlot(slot.request, slot.index, proof)
).to.be.revertedWith("Insufficient collateral")
})
it("is rejected when slot already filled", async function () {
await marketplace.fillSlot(slot.request, slot.index, proof)
await expect(
marketplace.fillSlot(slot.request, slot.index, proof)
).to.be.revertedWith("Slot already filled")
})
it("is rejected when request is unknown", async function () {
let unknown = exampleRequest()
await expect(
marketplace.fillSlot(requestId(unknown), 0, proof)
).to.be.revertedWith("Unknown request")
})
it("is rejected when request is expired", async function () {
switchAccount(client)
let expired = { ...request, expiry: now() - hours(1) }
await token.approve(marketplace.address, request.ask.reward)
await marketplace.requestStorage(expired)
switchAccount(host)
await expect(
marketplace.fillSlot(requestId(expired), slot.index, proof)
).to.be.revertedWith("Request expired")
})
it("is rejected when slot index not in range", async function () {
const invalid = request.content.erasure.totalNodes
await expect(
marketplace.fillSlot(slot.request, invalid, proof)
).to.be.revertedWith("Invalid slot")
})
})
describe("fulfilling request", function () {
const proof = hexlify(randomBytes(42))

View File

@ -39,8 +39,16 @@ function requestToArray(request) {
]
}
function slotId(slot) {
const types = "tuple(bytes32, uint256)"
const values = [slot.request, slot.index]
const encoding = defaultAbiCoder.encode([types], [values])
return keccak256(encoding)
}
module.exports = {
requestId,
slotId,
requestToArray,
askToArray,
}