refactor the DAL api for readability

This commit is contained in:
Eric Mastro 2022-12-01 21:42:27 +11:00
parent 407d51bc9a
commit 641892ccfa
No known key found for this signature in database
GPG Key ID: 141E3048D95A4E63
4 changed files with 206 additions and 249 deletions

View File

@ -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

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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 () {