mirror of
https://github.com/status-im/codex-contracts-eth.git
synced 2025-03-03 17:40:48 +00:00
[marketplace] Add slots
This commit is contained in:
parent
97ef1bcd9f
commit
141abce186
@ -10,6 +10,7 @@ contract Marketplace is Collateral, Proofs {
|
|||||||
MarketplaceFunds private funds;
|
MarketplaceFunds private funds;
|
||||||
mapping(bytes32 => Request) private requests;
|
mapping(bytes32 => Request) private requests;
|
||||||
mapping(bytes32 => RequestState) private requestState;
|
mapping(bytes32 => RequestState) private requestState;
|
||||||
|
mapping(bytes32 => Slot) private slots;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
IERC20 _token,
|
IERC20 _token,
|
||||||
@ -45,6 +46,30 @@ contract Marketplace is Collateral, Proofs {
|
|||||||
emit StorageRequested(id, request.ask);
|
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)
|
function fulfillRequest(bytes32 requestId, bytes calldata proof)
|
||||||
public
|
public
|
||||||
marketplaceInvariant
|
marketplaceInvariant
|
||||||
@ -127,8 +152,17 @@ contract Marketplace is Collateral, Proofs {
|
|||||||
address host;
|
address host;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Slot {
|
||||||
|
address host;
|
||||||
|
}
|
||||||
|
|
||||||
event StorageRequested(bytes32 requestId, Ask ask);
|
event StorageRequested(bytes32 requestId, Ask ask);
|
||||||
event RequestFulfilled(bytes32 indexed requestId);
|
event RequestFulfilled(bytes32 indexed requestId);
|
||||||
|
event SlotFilled(
|
||||||
|
bytes32 indexed requestId,
|
||||||
|
uint256 indexed slotIndex,
|
||||||
|
bytes32 indexed slotId
|
||||||
|
);
|
||||||
|
|
||||||
modifier marketplaceInvariant() {
|
modifier marketplaceInvariant() {
|
||||||
MarketplaceFunds memory oldFunds = funds;
|
MarketplaceFunds memory oldFunds = funds;
|
||||||
|
@ -4,7 +4,7 @@ const { expect } = require("chai")
|
|||||||
const { exampleRequest } = require("./examples")
|
const { exampleRequest } = require("./examples")
|
||||||
const { snapshot, revert, ensureMinimumBlockHeight } = require("./evm")
|
const { snapshot, revert, ensureMinimumBlockHeight } = require("./evm")
|
||||||
const { now, hours } = require("./time")
|
const { now, hours } = require("./time")
|
||||||
const { requestId, askToArray } = require("./ids")
|
const { requestId, slotId, askToArray } = require("./ids")
|
||||||
|
|
||||||
describe("Marketplace", function () {
|
describe("Marketplace", function () {
|
||||||
const collateral = 100
|
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 () {
|
describe("fulfilling request", function () {
|
||||||
const proof = hexlify(randomBytes(42))
|
const proof = hexlify(randomBytes(42))
|
||||||
|
|
||||||
|
@ -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 = {
|
module.exports = {
|
||||||
requestId,
|
requestId,
|
||||||
|
slotId,
|
||||||
requestToArray,
|
requestToArray,
|
||||||
askToArray,
|
askToArray,
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user