refactor the DAL api for readability
This commit is contained in:
parent
407d51bc9a
commit
641892ccfa
|
@ -10,7 +10,7 @@ import "./libs/DAL.sol";
|
|||
|
||||
contract Marketplace is Collateral, Proofs {
|
||||
using DAL for DAL.Database;
|
||||
using EnumerableSetExtensions for EnumerableSetExtensions.ClearableBytes32Set;
|
||||
using EnumerableSet for EnumerableSet.Bytes32Set;
|
||||
|
||||
uint256 public immutable collateral;
|
||||
MarketplaceFunds private funds;
|
||||
|
@ -32,47 +32,47 @@ contract Marketplace is Collateral, Proofs {
|
|||
}
|
||||
|
||||
function myRequests() public view returns (DAL.RequestId[] memory) {
|
||||
return DAL.toRequestIds(db.selectClient(msg.sender).activeRequests.values());
|
||||
DAL.Client storage client = db.select(DAL.ClientId.wrap(msg.sender));
|
||||
return DAL.toRequestIds(client.requests.values());
|
||||
}
|
||||
|
||||
function mySlots() public view returns (DAL.SlotId[] memory) {
|
||||
DAL.Host storage host = db.selectHost(msg.sender);
|
||||
return db.activeSlotsForHost(host);
|
||||
DAL.Host storage host = db.select(DAL.HostId.wrap(msg.sender));
|
||||
return db.activeSlots(host);
|
||||
}
|
||||
|
||||
|
||||
function requestStorage(Request calldata request)
|
||||
public
|
||||
marketplaceInvariant
|
||||
{
|
||||
require(request.client == msg.sender, "Invalid client address");
|
||||
|
||||
DAL.RequestId id = _toRequestId(request);
|
||||
require(!db.exists(id), "Request already exists");
|
||||
DAL.RequestId requestId = _toRequestId(request);
|
||||
|
||||
// DAL.Request storage dbRequest = DAL.Request(id, request.client, request.ask, request.content, request.expiry, request.nonce);
|
||||
if (!db.clientExists(request.client)) {
|
||||
db.insertClient(request.client);
|
||||
DAL.ClientId clientId = DAL.ClientId.wrap(request.client);
|
||||
if (!db.exists(clientId)) {
|
||||
db.insert(clientId);
|
||||
}
|
||||
DAL.Request storage dbRequest = db.insertRequest(id,
|
||||
request.client,
|
||||
request.ask,
|
||||
request.content,
|
||||
request.expiry,
|
||||
request.nonce);
|
||||
db.insertActiveRequestForClient(id);
|
||||
RequestContext storage context = _context(id);
|
||||
db.insert(requestId,
|
||||
clientId,
|
||||
request.ask,
|
||||
request.content,
|
||||
request.expiry,
|
||||
request.nonce);
|
||||
DAL.Client storage client = db.select(clientId);
|
||||
db.insert(client.requests, requestId);
|
||||
RequestContext storage context = _context(requestId);
|
||||
// set contract end time to `duration` from now (time request was created)
|
||||
context.endsAt = block.timestamp + request.ask.duration;
|
||||
_setProofEnd(_toEndId(id), context.endsAt);
|
||||
_createLock(_toLockId(id), request.expiry);
|
||||
_setProofEnd(_toEndId(requestId), context.endsAt);
|
||||
_createLock(_toLockId(requestId), request.expiry);
|
||||
|
||||
uint256 amount = price(dbRequest);
|
||||
uint256 amount = price(request);
|
||||
funds.received += amount;
|
||||
funds.balance += amount;
|
||||
transferFrom(msg.sender, amount);
|
||||
|
||||
emit StorageRequested(id, request.ask);
|
||||
emit StorageRequested(requestId, request.ask);
|
||||
}
|
||||
|
||||
function fillSlot(
|
||||
|
@ -80,7 +80,7 @@ contract Marketplace is Collateral, Proofs {
|
|||
uint256 slotIndex,
|
||||
bytes calldata proof
|
||||
) public requestMustAcceptProofs(requestId) marketplaceInvariant {
|
||||
DAL.Request storage request = db.selectRequest(requestId);
|
||||
DAL.Request storage request = db.select(requestId);
|
||||
require(slotIndex < request.ask.slots, "Invalid slot");
|
||||
|
||||
DAL.SlotId slotId = _toSlotId(requestId, slotIndex);
|
||||
|
@ -93,12 +93,15 @@ contract Marketplace is Collateral, Proofs {
|
|||
ProofId proofId = _toProofId(slotId);
|
||||
_expectProofs(proofId, _toEndId(requestId), request.ask.proofProbability);
|
||||
_submitProof(proofId, proof);
|
||||
if (!db.hostExists(msg.sender)) {
|
||||
db.insertHost(msg.sender);
|
||||
DAL.HostId hostId = DAL.HostId.wrap(msg.sender);
|
||||
if (!db.exists(hostId)) {
|
||||
db.insert(hostId);
|
||||
}
|
||||
db.insertSlot(DAL.Slot(slotId, msg.sender, false, requestId));
|
||||
db.insertActiveRequestForHost(msg.sender, requestId);
|
||||
db.insertActiveSlotForHost(slotId);
|
||||
DAL.Slot memory slot = DAL.Slot(slotId, hostId, false, requestId);
|
||||
db.insert(slot);
|
||||
DAL.Host storage host = db.select(hostId);
|
||||
db.insert(host.requests, requestId);
|
||||
db.insert(host.slots, slotId);
|
||||
|
||||
RequestContext storage context = _context(requestId);
|
||||
context.slotsFilled += 1;
|
||||
|
@ -118,7 +121,7 @@ contract Marketplace is Collateral, Proofs {
|
|||
marketplaceInvariant
|
||||
// TODO: restrict senders that can call this function
|
||||
{
|
||||
DAL.Slot storage slot = db.selectSlot(slotId);
|
||||
DAL.Slot storage slot = db.select(slotId);
|
||||
DAL.RequestId requestId = slot.requestId;
|
||||
RequestContext storage context = requestContexts[requestId];
|
||||
|
||||
|
@ -128,13 +131,16 @@ contract Marketplace is Collateral, Proofs {
|
|||
// not finalised.
|
||||
|
||||
_unexpectProofs(_toProofId(slotId));
|
||||
db.deleteActiveSlotForHost(slotId);
|
||||
address slotHost = slot.host;
|
||||
db.deleteSlot(slotId);
|
||||
DAL.Host storage host = db.select(slot.host);
|
||||
db.remove(host.slots, slotId);
|
||||
if (host.slots.length() == 0) {
|
||||
db.remove(host.requests, requestId);
|
||||
}
|
||||
db.remove(slot);
|
||||
context.slotsFilled -= 1;
|
||||
emit SlotFreed(requestId, slotId);
|
||||
|
||||
DAL.Request storage request = db.selectRequest(requestId);
|
||||
DAL.Request storage request = db.select(requestId);
|
||||
uint256 slotsLost = request.ask.slots - context.slotsFilled;
|
||||
if (
|
||||
slotsLost > request.ask.maxSlotLoss &&
|
||||
|
@ -143,11 +149,10 @@ contract Marketplace is Collateral, Proofs {
|
|||
context.state = RequestState.Failed;
|
||||
_setProofEnd(_toEndId(requestId), block.timestamp - 1);
|
||||
context.endsAt = block.timestamp - 1;
|
||||
// TODO: decide if we should *not* delete the slot above. If so, then
|
||||
// we'll need to clear the active slots, ie:
|
||||
// db.deleteAllActiveHostSlots(slotId);
|
||||
db.deleteActiveRequestForClient(requestId);
|
||||
db.deleteActiveRequestForHost(slotHost, requestId);
|
||||
|
||||
DAL.Client storage client = db.select(request.client);
|
||||
db.remove(client.requests, requestId);
|
||||
db.remove(host.requests, requestId);
|
||||
emit RequestFailed(requestId);
|
||||
|
||||
// TODO: burn all remaining slot collateral (note: slot collateral not
|
||||
|
@ -162,28 +167,34 @@ contract Marketplace is Collateral, Proofs {
|
|||
{
|
||||
require(_isFinished(requestId), "Contract not ended");
|
||||
RequestContext storage context = _context(requestId);
|
||||
DAL.Request storage request = db.selectRequest(requestId);
|
||||
DAL.Request storage request = db.select(requestId);
|
||||
DAL.SlotId slotId = _toSlotId(requestId, slotIndex);
|
||||
DAL.Slot storage slot = db.selectSlot(slotId);
|
||||
DAL.Slot storage slot = db.select(slotId);
|
||||
require(!slot.hostPaid, "Already paid");
|
||||
|
||||
context.state = RequestState.Finished;
|
||||
db.deleteActiveSlotForHost(slotId);
|
||||
db.deleteActiveRequestForClient(requestId);
|
||||
DAL.Host storage host = db.select(slot.host);
|
||||
db.remove(host.slots, slotId);
|
||||
if (host.slots.length() == 0) {
|
||||
db.remove(host.requests, requestId);
|
||||
}
|
||||
DAL.Client storage client = db.select(request.client);
|
||||
// TODO: do we want to remove the client's "active" request on first slot payout?
|
||||
db.remove(client.requests, requestId);
|
||||
uint256 amount = pricePerSlot(request);
|
||||
funds.sent += amount;
|
||||
funds.balance -= amount;
|
||||
slot.hostPaid = true;
|
||||
require(token.transfer(slot.host, amount), "Payment failed");
|
||||
require(token.transfer(DAL.HostId.unwrap(slot.host), amount), "Payment failed");
|
||||
}
|
||||
|
||||
/// @notice Withdraws storage request funds back to the client that deposited them.
|
||||
/// @dev Request must be expired, must be in RequestState.New, and the transaction must originate from the depositer address.
|
||||
/// @param requestId the id of the request
|
||||
function withdrawFunds(DAL.RequestId requestId) public marketplaceInvariant {
|
||||
DAL.Request storage request = db.selectRequest(requestId);
|
||||
DAL.Request storage request = db.select(requestId);
|
||||
require(block.timestamp > request.expiry, "Request not yet timed out");
|
||||
require(request.client == msg.sender, "Invalid client address");
|
||||
require(DAL.ClientId.unwrap(request.client) == msg.sender, "Invalid client address");
|
||||
RequestContext storage context = _context(requestId);
|
||||
require(context.state == RequestState.New, "Invalid state");
|
||||
|
||||
|
@ -192,7 +203,9 @@ contract Marketplace is Collateral, Proofs {
|
|||
context.state = RequestState.Cancelled;
|
||||
// TODO: double-check that we don't want to _removeAllHostSlots() here.
|
||||
// @markspanbroek?
|
||||
db.deleteActiveRequestForClient(requestId);
|
||||
DAL.Client storage client = db.select(request.client);
|
||||
db.remove(client.requests, requestId);
|
||||
// db.deleteActiveRequestForClient(requestId);
|
||||
// TODO: handle dangling DAL.RequestId in activeHostRequests (for address)
|
||||
emit RequestCancelled(requestId);
|
||||
|
||||
|
@ -214,7 +227,7 @@ contract Marketplace is Collateral, Proofs {
|
|||
return
|
||||
context.state == RequestState.Cancelled ||
|
||||
(context.state == RequestState.New &&
|
||||
block.timestamp > db.selectRequest(requestId).expiry);
|
||||
block.timestamp > db.select(requestId).expiry);
|
||||
}
|
||||
|
||||
/// @notice Return true if the request state is RequestState.Finished or if the request duration has elapsed and the request was started.
|
||||
|
@ -238,7 +251,7 @@ contract Marketplace is Collateral, Proofs {
|
|||
view
|
||||
returns (DAL.RequestId)
|
||||
{
|
||||
DAL.Slot memory slot = db.selectSlot(slotId);
|
||||
DAL.Slot storage slot = db.select(slotId);
|
||||
require(!DAL.isDefault(slot.requestId), "Missing request id");
|
||||
return slot.requestId;
|
||||
}
|
||||
|
@ -252,8 +265,9 @@ contract Marketplace is Collateral, Proofs {
|
|||
return _isCancelled(requestId);
|
||||
}
|
||||
|
||||
function _host(DAL.SlotId slotId) internal view returns (address) {
|
||||
return db.selectSlot(slotId).host;
|
||||
function _host(DAL.SlotId slotId) internal view returns (DAL.HostId) {
|
||||
DAL.Slot storage slot = _slot(slotId);
|
||||
return slot.host;
|
||||
}
|
||||
|
||||
function _request(DAL.RequestId requestId)
|
||||
|
@ -261,11 +275,11 @@ contract Marketplace is Collateral, Proofs {
|
|||
view
|
||||
returns (DAL.Request storage)
|
||||
{
|
||||
return db.selectRequest(requestId);
|
||||
return db.select(requestId);
|
||||
}
|
||||
|
||||
function _slot(DAL.SlotId slotId) internal view returns (DAL.Slot storage) {
|
||||
return db.selectSlot(slotId);
|
||||
return db.select(slotId);
|
||||
}
|
||||
|
||||
function _context(DAL.RequestId requestId)
|
||||
|
@ -285,7 +299,7 @@ contract Marketplace is Collateral, Proofs {
|
|||
}
|
||||
|
||||
function proofEnd(DAL.SlotId slotId) public view returns (uint256) {
|
||||
return requestEnd(db.selectSlot(slotId).requestId);
|
||||
return requestEnd(db.select(slotId).requestId);
|
||||
}
|
||||
|
||||
function requestEnd(DAL.RequestId requestId) public view returns (uint256) {
|
||||
|
@ -305,11 +319,11 @@ contract Marketplace is Collateral, Proofs {
|
|||
return numSlots * duration * reward;
|
||||
}
|
||||
|
||||
function _price(DAL.Request storage request) internal view returns (uint256) {
|
||||
function price(Request memory request) private pure returns (uint256) {
|
||||
return _price(request.ask.slots, request.ask.duration, request.ask.reward);
|
||||
}
|
||||
|
||||
function price(DAL.Request storage request) private view returns (uint256) {
|
||||
function _price(DAL.Request storage request) private view returns (uint256) {
|
||||
return _price(request.ask.slots, request.ask.duration, request.ask.reward);
|
||||
}
|
||||
|
||||
|
@ -356,17 +370,6 @@ contract Marketplace is Collateral, Proofs {
|
|||
return DAL.RequestId.wrap(keccak256(abi.encode(request)));
|
||||
}
|
||||
|
||||
// function _toSlotIds(bytes32[] memory array)
|
||||
// private
|
||||
// pure
|
||||
// returns (DAL.SlotId[] memory result)
|
||||
// {
|
||||
// // solhint-disable-next-line no-inline-assembly
|
||||
// assembly {
|
||||
// result := array
|
||||
// }
|
||||
// }
|
||||
|
||||
function _toSlotId(DAL.RequestId requestId, uint256 slotIndex)
|
||||
internal
|
||||
pure
|
||||
|
@ -387,22 +390,6 @@ contract Marketplace is Collateral, Proofs {
|
|||
return EndId.wrap(DAL.RequestId.unwrap(requestId));
|
||||
}
|
||||
|
||||
// function _notEqual(DAL.RequestId a, uint256 b) internal pure returns (bool) {
|
||||
// return DAL.RequestId.unwrap(a) != bytes32(b);
|
||||
// }
|
||||
|
||||
// struct Client {
|
||||
// address addr; // PK
|
||||
|
||||
// EnumerableSetExtensions.ClearableBytes32Set activeRequests;
|
||||
// }
|
||||
|
||||
// struct Host {
|
||||
// address addr; // PK
|
||||
|
||||
// EnumerableSetExtensions.ClearableBytes32Set activeSlots;
|
||||
// }
|
||||
|
||||
struct Request {
|
||||
address client;
|
||||
DAL.Ask ask;
|
||||
|
@ -411,37 +398,6 @@ contract Marketplace is Collateral, Proofs {
|
|||
bytes32 nonce; // random nonce to differentiate between similar requests
|
||||
}
|
||||
|
||||
// struct Slot {
|
||||
// address host;
|
||||
// bool hostPaid;
|
||||
// DAL.RequestId requestId;
|
||||
// }
|
||||
|
||||
// struct Ask {
|
||||
// uint64 slots; // the number of requested slots
|
||||
// uint256 slotSize; // amount of storage per slot (in number of bytes)
|
||||
// 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 {
|
||||
// string cid; // content id (if part of a larger set, the chunk cid)
|
||||
// Erasure erasure; // Erasure coding attributes
|
||||
// PoR por; // Proof of Retrievability parameters
|
||||
// }
|
||||
|
||||
// struct Erasure {
|
||||
// uint64 totalChunks; // the total number of chunks in the larger data set
|
||||
// }
|
||||
|
||||
// struct PoR {
|
||||
// bytes u; // parameters u_1..u_s
|
||||
// bytes publicKey; // public key
|
||||
// bytes name; // random name
|
||||
// }
|
||||
|
||||
enum RequestState {
|
||||
New, // [default] waiting to fill slots
|
||||
Started, // all slots filled, accepting regular proofs
|
||||
|
|
|
@ -37,7 +37,11 @@ contract Storage is Collateral, Marketplace {
|
|||
|
||||
function getRequest(DAL.RequestId requestId) public view returns (Marketplace.Request memory) {
|
||||
DAL.Request storage request = _request(requestId);
|
||||
return Marketplace.Request(request.client, request.ask, request.content, request.expiry, request.nonce);
|
||||
return Request(DAL.ClientId.unwrap(request.client),
|
||||
request.ask,
|
||||
request.content,
|
||||
request.expiry,
|
||||
request.nonce);
|
||||
// return _request(requestId);
|
||||
}
|
||||
|
||||
|
@ -46,8 +50,8 @@ contract Storage is Collateral, Marketplace {
|
|||
// return _slot(slotId);
|
||||
}
|
||||
|
||||
function getHost(DAL.SlotId slotId) public view returns (address) {
|
||||
return _slot(slotId).host;
|
||||
function getHost(DAL.SlotId slotId) public view returns (DAL.HostId) {
|
||||
return _host(slotId);
|
||||
// return _host(slotId);
|
||||
}
|
||||
|
||||
|
@ -90,7 +94,7 @@ contract Storage is Collateral, Marketplace {
|
|||
{
|
||||
ProofId proofId = _toProofId(slotId);
|
||||
_markProofAsMissing(proofId, period);
|
||||
address host = _host(slotId);
|
||||
address host = DAL.HostId.unwrap(_host(slotId));
|
||||
if (_missed(_toProofId(slotId)) % slashMisses == 0) {
|
||||
_slash(host, slashPercentage);
|
||||
|
||||
|
|
|
@ -2,46 +2,46 @@
|
|||
// inspired by: https://bitbucket.org/rhitchens2/soliditystoragepatterns/src/master/OneToMany.sol
|
||||
pragma solidity ^0.8.8;
|
||||
|
||||
import "./EnumerableSetExtensions.sol";
|
||||
import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
|
||||
import "../Marketplace.sol";
|
||||
import "./Utils.sol";
|
||||
|
||||
library DAL {
|
||||
|
||||
using EnumerableSet for EnumerableSet.Bytes32Set;
|
||||
using EnumerableSet for EnumerableSet.AddressSet;
|
||||
using EnumerableSetExtensions for EnumerableSetExtensions.ClearableBytes32Set;
|
||||
|
||||
type RequestId is bytes32;
|
||||
type SlotId is bytes32;
|
||||
type ClientId is address;
|
||||
type HostId is address;
|
||||
|
||||
struct Client {
|
||||
address addr; // PK
|
||||
ClientId id; // PK
|
||||
|
||||
EnumerableSetExtensions.ClearableBytes32Set activeRequests;
|
||||
EnumerableSet.Bytes32Set requests;
|
||||
}
|
||||
|
||||
struct Host {
|
||||
address addr; // PK
|
||||
HostId id; // PK
|
||||
|
||||
EnumerableSetExtensions.ClearableBytes32Set activeSlots;
|
||||
EnumerableSetExtensions.ClearableBytes32Set activeRequests;
|
||||
EnumerableSet.Bytes32Set slots;
|
||||
EnumerableSet.Bytes32Set requests;
|
||||
}
|
||||
|
||||
struct Request {
|
||||
RequestId id;
|
||||
address client;
|
||||
ClientId client;
|
||||
Ask ask;
|
||||
Content content;
|
||||
uint256 expiry; // time at which this request expires
|
||||
bytes32 nonce; // random nonce to differentiate between similar requests
|
||||
|
||||
EnumerableSetExtensions.ClearableBytes32Set slots;
|
||||
EnumerableSet.Bytes32Set slots;
|
||||
}
|
||||
|
||||
struct Slot {
|
||||
SlotId id;
|
||||
address host;
|
||||
HostId host;
|
||||
bool hostPaid;
|
||||
RequestId requestId;
|
||||
}
|
||||
|
@ -74,98 +74,95 @@ library DAL {
|
|||
struct Database {
|
||||
mapping(RequestId => Request) requests;
|
||||
mapping(SlotId => Slot) slots;
|
||||
mapping(address => Client) clients;
|
||||
mapping(address => Host) hosts;
|
||||
mapping(ClientId => Client) clients;
|
||||
mapping(HostId => Host) hosts;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// *** CREATE OPERATIONS *** ///
|
||||
|
||||
function insertRequest(Database storage db,
|
||||
RequestId requestId,
|
||||
address client,
|
||||
Ask memory ask,
|
||||
Content memory content,
|
||||
uint256 expiry,
|
||||
bytes32 nonce)
|
||||
function insert(Database storage db,
|
||||
RequestId requestId,
|
||||
ClientId clientId,
|
||||
Ask memory ask,
|
||||
Content memory content,
|
||||
uint256 expiry,
|
||||
bytes32 nonce)
|
||||
internal
|
||||
returns (DAL.Request storage)
|
||||
{
|
||||
require(clientExists(db, client), "client does not exist");
|
||||
require(!isDefault(requestId), "request id required");
|
||||
require(!_isDefault(clientId), "client address required");
|
||||
require(exists(db, clientId), "client does not exist");
|
||||
require(!exists(db, requestId), "request already exists");
|
||||
|
||||
Request storage r = db.requests[requestId];
|
||||
r.id = requestId;
|
||||
r.client = client;
|
||||
r.client = clientId;
|
||||
r.ask = ask;
|
||||
r.content = content;
|
||||
r.expiry = expiry;
|
||||
r.nonce = nonce;
|
||||
return r;
|
||||
}
|
||||
|
||||
function insertClient(Database storage db, address client) internal {
|
||||
// NOTE: by default db.clients[client].activeRequests already exists but has a default value
|
||||
db.clients[client].addr = client;
|
||||
function insert(Database storage db, ClientId clientId) internal {
|
||||
require (!exists(db, clientId), "client already exists");
|
||||
require (!_isDefault(clientId), "address required");
|
||||
Client storage c = db.clients[clientId];
|
||||
c.id = clientId;
|
||||
// NOTE: by default db.clients[client].requests already exists but has a default value
|
||||
}
|
||||
|
||||
function insertHost(Database storage db, address host) internal {
|
||||
// NOTE: by default db.hosts[host].activeSlots already exists but has a default value
|
||||
db.hosts[host].addr = host;
|
||||
function insert(Database storage db, HostId hostId) internal {
|
||||
require (!exists(db, hostId), "host already exists");
|
||||
require (!_isDefault(hostId), "address required");
|
||||
Host storage h = db.hosts[hostId];
|
||||
h.id = hostId;
|
||||
// NOTE: by default db.hosts[host].slots already exists but has a default value
|
||||
}
|
||||
|
||||
function insertSlot(Database storage db, Slot memory slot) internal {
|
||||
function insert(Database storage db, Slot memory slot ) internal {
|
||||
require(!_isDefault(slot.id), "slot id required");
|
||||
require(!isDefault(slot.requestId), "request id required");
|
||||
require(exists(db, slot.requestId), "request does not exist");
|
||||
require(!exists(db, slot.id), "slot already exists");
|
||||
require(hostExists(db, slot.host), "host does not exist");
|
||||
require(exists(db, slot.host), "host does not exist");
|
||||
db.slots[slot.id] = slot;
|
||||
|
||||
Request storage request = db.requests[slot.requestId];
|
||||
request.slots.add(SlotId.unwrap(slot.id));
|
||||
}
|
||||
|
||||
function insertActiveRequestForClient(Database storage db,
|
||||
RequestId requestId)
|
||||
function insert(Database storage db,
|
||||
EnumerableSet.Bytes32Set storage requests,
|
||||
RequestId requestId)
|
||||
internal
|
||||
{
|
||||
require(exists(db, requestId), "request does not exist");
|
||||
Request storage request = db.requests[requestId];
|
||||
require(clientExists(db, request.client), "client does not exist");
|
||||
|
||||
Client storage client = db.clients[request.client];
|
||||
client.activeRequests.add(RequestId.unwrap(requestId));
|
||||
}
|
||||
function insertActiveRequestForHost(Database storage db,
|
||||
address host,
|
||||
RequestId requestId)
|
||||
internal
|
||||
{
|
||||
require(exists(db, requestId), "request does not exist");
|
||||
require(hostExists(db, host), "host does not exist");
|
||||
|
||||
Host storage h = db.hosts[host];
|
||||
h.activeRequests.add(RequestId.unwrap(requestId));
|
||||
requests.add(RequestId.unwrap(requestId));
|
||||
}
|
||||
|
||||
function insertActiveSlotForHost(Database storage db, SlotId slotId)
|
||||
function insert(Database storage db,
|
||||
EnumerableSet.Bytes32Set storage slots,
|
||||
SlotId slotId)
|
||||
internal
|
||||
{
|
||||
require(exists(db, slotId), "slot does not exist");
|
||||
Slot storage slot = db.slots[slotId];
|
||||
require(hostExists(db, slot.host), "host does not exist");
|
||||
require(exists(db, slot.host), "host does not exist");
|
||||
Host storage host = db.hosts[slot.host];
|
||||
require(host.activeRequests.contains(RequestId.unwrap(slot.requestId)),
|
||||
require(host.requests.contains(RequestId.unwrap(slot.requestId)),
|
||||
"slot request not active");
|
||||
|
||||
host.activeSlots.add(SlotId.unwrap(slotId));
|
||||
slots.add(SlotId.unwrap(slotId));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// *** READ OPERATIONS *** ///
|
||||
|
||||
function selectRequest(Database storage db, RequestId requestId)
|
||||
function select(Database storage db, RequestId requestId)
|
||||
internal
|
||||
view
|
||||
returns (Request storage)
|
||||
|
@ -174,7 +171,7 @@ library DAL {
|
|||
return db.requests[requestId];
|
||||
}
|
||||
|
||||
function selectSlot(Database storage db, SlotId slotId)
|
||||
function select(Database storage db, SlotId slotId)
|
||||
internal
|
||||
view
|
||||
returns (Slot storage)
|
||||
|
@ -183,22 +180,22 @@ library DAL {
|
|||
return db.slots[slotId];
|
||||
}
|
||||
|
||||
function selectClient(Database storage db, address addr)
|
||||
function select(Database storage db, ClientId clientId)
|
||||
internal
|
||||
view
|
||||
returns (Client storage)
|
||||
{
|
||||
require(clientExists(db, addr), "Client does not exist");
|
||||
return db.clients[addr];
|
||||
require(exists(db, clientId), "Client does not exist");
|
||||
return db.clients[clientId];
|
||||
}
|
||||
|
||||
function selectHost(Database storage db, address addr)
|
||||
function select(Database storage db, HostId hostId)
|
||||
internal
|
||||
view
|
||||
returns (Host storage)
|
||||
{
|
||||
require(hostExists(db, addr), "Host does not exist");
|
||||
return db.hosts[addr];
|
||||
require(exists(db, hostId), "Host does not exist");
|
||||
return db.hosts[hostId];
|
||||
}
|
||||
|
||||
function exists(Database storage db, RequestId requestId)
|
||||
|
@ -206,7 +203,8 @@ library DAL {
|
|||
view
|
||||
returns (bool)
|
||||
{
|
||||
return db.requests[requestId].client != address(0);
|
||||
Request storage request = db.requests[requestId];
|
||||
return !isDefault(request.id) && !_isDefault(request.client);
|
||||
}
|
||||
|
||||
function exists(Database storage db, SlotId slotId)
|
||||
|
@ -214,23 +212,27 @@ library DAL {
|
|||
view
|
||||
returns (bool)
|
||||
{
|
||||
return !isDefault(db.slots[slotId].requestId);
|
||||
Slot storage slot = db.slots[slotId];
|
||||
Request storage request = db.requests[slot.requestId];
|
||||
return request.slots.contains(SlotId.unwrap(slotId)) &&
|
||||
!_isDefault(slot.id) &&
|
||||
!isDefault(slot.requestId);
|
||||
}
|
||||
|
||||
function clientExists(Database storage db, address client)
|
||||
function exists(Database storage db, ClientId clientId)
|
||||
internal
|
||||
view
|
||||
returns (bool)
|
||||
{
|
||||
return db.clients[client].addr != address(0);
|
||||
return !_isDefault(db.clients[clientId].id);
|
||||
}
|
||||
|
||||
function hostExists(Database storage db, address host)
|
||||
function exists(Database storage db, HostId hostId)
|
||||
internal
|
||||
view
|
||||
returns (bool)
|
||||
{
|
||||
return db.hosts[host].addr != address(0);
|
||||
return !_isDefault(db.hosts[hostId].id);
|
||||
}
|
||||
|
||||
|
||||
|
@ -238,107 +240,76 @@ library DAL {
|
|||
|
||||
/// *** DELETE OPERATIONS *** ///
|
||||
|
||||
function deleteRequest(Database storage db, RequestId requestId) internal {
|
||||
require(exists(db, requestId), "request does not exist");
|
||||
Request storage request = db.requests[requestId];
|
||||
function remove(Database storage db, Request storage request) internal {
|
||||
require(request.slots.length() == 0, "references slots");
|
||||
require(clientExists(db, request.client), "client does not exist");
|
||||
require(exists(db, request.client), "client does not exist");
|
||||
Client storage client = db.clients[request.client];
|
||||
bytes32 bRequestId = RequestId.unwrap(requestId);
|
||||
require(!client.activeRequests.contains(bRequestId), "active request refs");
|
||||
bytes32 bRequestId = RequestId.unwrap(request.id);
|
||||
require(!client.requests.contains(bRequestId), "active request refs");
|
||||
|
||||
delete db.requests[requestId];
|
||||
delete db.requests[request.id];
|
||||
}
|
||||
|
||||
function deleteClient(Database storage db, address addr) internal {
|
||||
require(clientExists(db, addr), "client does not exist");
|
||||
Client storage c = db.clients[addr];
|
||||
require(c.activeRequests.length() == 0, "active request refs");
|
||||
function remove(Database storage db, Client storage client) internal {
|
||||
require(client.requests.length() == 0, "active request refs");
|
||||
|
||||
delete db.clients[addr];
|
||||
delete db.clients[client.id];
|
||||
}
|
||||
|
||||
function deleteHost(Database storage db, address addr) internal {
|
||||
require(hostExists(db, addr), "host does not exist");
|
||||
Host storage h = db.hosts[addr];
|
||||
require(h.activeSlots.length() == 0, "active slot refs");
|
||||
function remove(Database storage db, Host storage host) internal {
|
||||
require(host.slots.length() == 0, "active slot refs");
|
||||
|
||||
delete db.hosts[addr];
|
||||
delete db.hosts[host.id];
|
||||
}
|
||||
|
||||
function deleteSlot(Database storage db, SlotId slotId) internal {
|
||||
require(exists(db, slotId), "slot does not exist");
|
||||
Slot storage slot = db.slots[slotId];
|
||||
function remove(Database storage db, Slot storage slot) internal {
|
||||
require(exists(db, slot.requestId), "request does not exist");
|
||||
Host storage host = db.hosts[slot.host];
|
||||
bytes32 bSlotId = SlotId.unwrap(slotId);
|
||||
require(!host.activeSlots.contains(bSlotId), "active slot refs");
|
||||
bytes32 bSlotId = SlotId.unwrap(slot.id);
|
||||
require(!host.slots.contains(bSlotId), "active slot refs");
|
||||
|
||||
Request storage request = db.requests[slot.requestId];
|
||||
request.slots.remove(bSlotId);
|
||||
delete db.slots[slotId];
|
||||
delete db.slots[slot.id];
|
||||
}
|
||||
|
||||
function deleteActiveRequestForClient(Database storage db,
|
||||
RequestId requestId)
|
||||
function remove(Database storage db,
|
||||
EnumerableSet.Bytes32Set storage requests,
|
||||
RequestId requestId)
|
||||
internal
|
||||
{
|
||||
require(exists(db, requestId), "request does not exist");
|
||||
Request storage request = db.requests[requestId];
|
||||
require(clientExists(db, request.client), "client does not exist");
|
||||
|
||||
Client storage client = db.clients[request.client];
|
||||
client.activeRequests.remove(RequestId.unwrap(requestId));
|
||||
requests.remove(RequestId.unwrap(requestId));
|
||||
}
|
||||
|
||||
function deleteActiveRequestForHost(Database storage db,
|
||||
address host,
|
||||
RequestId requestId)
|
||||
function remove(Database storage db,
|
||||
EnumerableSet.Bytes32Set storage slots,
|
||||
SlotId slotId)
|
||||
internal
|
||||
{
|
||||
require(exists(db, requestId), "request does not exist");
|
||||
require(hostExists(db, host), "host does not exist");
|
||||
// NOTE: we are not enforcing relationship integrity with
|
||||
// host.activeRequests as a workaround to avoid iterating all activeSlots
|
||||
// and removing them. The result of this is that there may
|
||||
// exist "dangling" host.activeSlots that do not have a corresponding
|
||||
// activeRequest, which should be considered when reading the values.
|
||||
// Because of this, a join between activeSlots and activeRequests should be
|
||||
// performed to get an accurate picture, as in `activeSlotsForHost`.
|
||||
|
||||
Host storage h = db.hosts[host];
|
||||
h.activeRequests.remove(RequestId.unwrap(requestId));
|
||||
}
|
||||
|
||||
function deleteActiveSlotForHost(Database storage db,
|
||||
SlotId slotId)
|
||||
internal
|
||||
returns (bool success)
|
||||
{
|
||||
require(exists(db, slotId), "slot does not exist");
|
||||
Slot storage slot = db.slots[slotId];
|
||||
require(hostExists(db, slot.host), "host does not exist");
|
||||
|
||||
Host storage host = db.hosts[slot.host];
|
||||
success = host.activeSlots.remove(SlotId.unwrap(slotId));
|
||||
slots.remove(SlotId.unwrap(slotId));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// CALCULATED PROPERTIES
|
||||
/// *** CALCULATED PROPERTIES *** ///
|
||||
|
||||
// WARNING: calling this in a transaction may cause an out of gas exception
|
||||
function activeSlotsForHost(Database storage db, Host storage host)
|
||||
function activeSlots(Database storage db, Host storage host)
|
||||
internal
|
||||
view
|
||||
returns (SlotId[] memory)
|
||||
{
|
||||
bytes32[] memory result = new bytes32[](host.activeSlots.length());
|
||||
// perform an inner join on host.slots and host.requests
|
||||
bytes32[] memory result = new bytes32[](host.slots.length());
|
||||
uint256 counter = 0;
|
||||
for (uint256 i = 0; i < host.activeSlots.length(); i++) {
|
||||
bytes32 slotId = host.activeSlots.at(i);
|
||||
Slot storage slot = selectSlot(db, SlotId.wrap(slotId));
|
||||
if (host.activeRequests.contains(RequestId.unwrap(slot.requestId))) {
|
||||
for (uint256 i = 0; i < host.slots.length(); i++) {
|
||||
bytes32 slotId = host.slots.at(i);
|
||||
Slot storage slot = select(db, SlotId.wrap(slotId));
|
||||
if (host.requests.contains(RequestId.unwrap(slot.requestId))) {
|
||||
result[counter] = slotId;
|
||||
counter++;
|
||||
}
|
||||
|
@ -346,7 +317,9 @@ library DAL {
|
|||
return toSlotIds(Utils.resize(result, counter));
|
||||
}
|
||||
|
||||
/// CONVERSIONS
|
||||
|
||||
|
||||
/// *** CONVERSIONS *** ///
|
||||
|
||||
function toRequestIds(bytes32[] memory array)
|
||||
internal
|
||||
|
@ -378,11 +351,33 @@ library DAL {
|
|||
return SlotId.wrap(keccak256(abi.encode(requestId, slotIndex)));
|
||||
}
|
||||
|
||||
/// COMPARISONS
|
||||
/// *** COMPARISONS *** ///
|
||||
|
||||
function isDefault(RequestId requestId) internal pure returns (bool) {
|
||||
return equals(requestId, RequestId.wrap(0));
|
||||
}
|
||||
|
||||
function _isDefault(SlotId slotId) private pure returns (bool) {
|
||||
return equals(slotId, SlotId.wrap(0));
|
||||
}
|
||||
|
||||
function _isDefault(address addr) private pure returns (bool) {
|
||||
return addr == address(0);
|
||||
}
|
||||
|
||||
function _isDefault (ClientId clientId) private pure returns (bool) {
|
||||
return _isDefault(ClientId.unwrap(clientId));
|
||||
}
|
||||
|
||||
function _isDefault (HostId hostId) private pure returns (bool) {
|
||||
return _isDefault(HostId.unwrap(hostId));
|
||||
}
|
||||
|
||||
function equals(RequestId a, RequestId b) internal pure returns (bool) {
|
||||
return RequestId.unwrap(a) == RequestId.unwrap(b);
|
||||
}
|
||||
|
||||
function equals(SlotId a, SlotId b) internal pure returns (bool) {
|
||||
return SlotId.unwrap(a) == SlotId.unwrap(b);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -104,7 +104,7 @@ describe("Marketplace", function () {
|
|||
await token.approve(marketplace.address, price(request) * 2)
|
||||
await marketplace.requestStorage(request)
|
||||
await expect(marketplace.requestStorage(request)).to.be.revertedWith(
|
||||
"Request already exists"
|
||||
"request already exists"
|
||||
)
|
||||
})
|
||||
})
|
||||
|
@ -779,6 +779,8 @@ describe("Marketplace", function () {
|
|||
await waitUntilFailed(marketplace, request, slot)
|
||||
switchAccount(host)
|
||||
expect(await marketplace.mySlots()).to.deep.equal([])
|
||||
switchAccount(host2)
|
||||
expect(await marketplace.mySlots()).to.deep.equal([])
|
||||
})
|
||||
|
||||
it("doesn't remove active slots for hosts in request that didn't fail", async function () {
|
||||
|
|
Loading…
Reference in New Issue