mirror of
https://github.com/logos-storage/logos-storage-contracts-eth.git
synced 2026-01-21 06:23:06 +00:00
WIP: initial implementation of one-to-many
Needs to be cleaned up and reorganised.
This commit is contained in:
parent
d70efad7bd
commit
d4a0521f33
@ -8,6 +8,9 @@ import "./Collateral.sol";
|
||||
import "./Proofs.sol";
|
||||
import "./libs/SetMap.sol";
|
||||
import "./libs/Utils.sol";
|
||||
import "./libs/ORM2.sol";
|
||||
|
||||
import "hardhat/console.sol"; // DELETE ME
|
||||
|
||||
contract Marketplace is Collateral, Proofs {
|
||||
using EnumerableSet for EnumerableSet.Bytes32Set;
|
||||
@ -15,6 +18,7 @@ contract Marketplace is Collateral, Proofs {
|
||||
using SetMap for SetMap.Bytes32SetMap;
|
||||
using SetMap for SetMap.AddressBytes32SetMap;
|
||||
using SetMap for SetMap.Bytes32AddressSetMap;
|
||||
using ORM2 for ORM2.OneToMany;
|
||||
|
||||
type RequestId is bytes32;
|
||||
type SlotId is bytes32;
|
||||
@ -24,10 +28,19 @@ contract Marketplace is Collateral, Proofs {
|
||||
mapping(RequestId => Request) private requests;
|
||||
mapping(RequestId => RequestContext) private requestContexts;
|
||||
mapping(SlotId => Slot) private slots;
|
||||
SetMap.AddressBytes32SetMap private activeRequestsForClients; // purchasing
|
||||
SetMap.Bytes32AddressSetMap private activeRequestsForHosts; // purchasing
|
||||
// SetMap.AddressBytes32SetMap private activeRequestsForClients; // purchasing
|
||||
//SetMap.Bytes32AddressSetMap private activeRequestsForHosts; // sales
|
||||
// ORM.Table private activeRequestsForHosts; // sales
|
||||
SetMap.Bytes32SetMap private activeSlots; // sales
|
||||
|
||||
ORM2.OneToMany private activeClientRequests;
|
||||
|
||||
// address => RequestId
|
||||
ORM2.OneToMany private activeHostRequests;
|
||||
// RequestId => SlotId
|
||||
ORM2.OneToMany private activeRequestSlots;
|
||||
|
||||
|
||||
constructor(
|
||||
IERC20 _token,
|
||||
uint256 _collateral,
|
||||
@ -43,53 +56,61 @@ contract Marketplace is Collateral, Proofs {
|
||||
}
|
||||
|
||||
function myRequests() public view returns (RequestId[] memory) {
|
||||
SetMap.AddressBytes32SetMapKey key = _toAddressSetMapKey(msg.sender);
|
||||
RequestId[] memory requestIds = _toRequestIds(activeRequestsForClients.values(key));
|
||||
RequestId[] memory result = new RequestId[](requestIds.length);
|
||||
uint8 counter = 0;
|
||||
uint256 counter = 0;
|
||||
bytes32[] storage requestIds =
|
||||
activeClientRequests.getManyKeys(_toBytes32(msg.sender));
|
||||
bytes32[] memory result = new bytes32[](requestIds.length);
|
||||
for (uint8 i = 0; i < requestIds.length; i++) {
|
||||
if (!_isCancelled(requestIds[i])) {
|
||||
result[counter] = requestIds[i];
|
||||
counter++;
|
||||
// There may exist slots that are still "active", but are part of a request
|
||||
// that is expired but has not been set to the cancelled state yet. In that
|
||||
// case, return an empty array.
|
||||
// TODO: remove cancelled lookups https://discord.com/channels/@me/958221374076366898/1044947242013954140
|
||||
bytes32 requestId = requestIds[i];
|
||||
if (_isCancelled(RequestId.wrap(requestId))) {
|
||||
continue;
|
||||
}
|
||||
result[counter] = requestId;
|
||||
counter++;
|
||||
}
|
||||
return _toRequestIds(Utils._resize(_toBytes32s(result), counter));
|
||||
return _toRequestIds(Utils._resize(result, counter));
|
||||
}
|
||||
|
||||
function requestsForHost(address host) public view returns(RequestId[] memory) {
|
||||
EnumerableSet.Bytes32Set storage keys = activeRequestsForHosts.keys();
|
||||
uint256 keyLength = keys.length();
|
||||
RequestId[] memory result = new RequestId[](keyLength);
|
||||
|
||||
uint8 counter = 0; // should be big enough
|
||||
for (uint8 i = 0; i < keyLength; i++) {
|
||||
RequestId requestId = RequestId.wrap(keys.at(i));
|
||||
SetMap.Bytes32AddressSetMapKey key = _toBytes32AddressSetMapKey(requestId);
|
||||
if (activeRequestsForHosts.contains(key, host) &&
|
||||
!_isCancelled(requestId))
|
||||
{
|
||||
result[counter] = requestId;
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
return _toRequestIds(Utils._resize(_toBytes32s(result), counter));
|
||||
}
|
||||
|
||||
function mySlots(RequestId requestId)
|
||||
function mySlots()
|
||||
public
|
||||
view
|
||||
returns (SlotId[] memory)
|
||||
{
|
||||
// There may exist slots that are still "active", but are part of a request
|
||||
// that is expired but has not been set to the cancelled state yet. In that
|
||||
// case, return an empty array.
|
||||
if (_isCancelled(requestId)) {
|
||||
SlotId[] memory result;
|
||||
return result;
|
||||
uint256 counter = 0;
|
||||
uint256 totalSlots = activeRequestSlots.getTotalManyCount(); // set this bigger than our possible filtered list size
|
||||
console.log("[mySlots] total slotIds: ", totalSlots);
|
||||
if (totalSlots == 0) {
|
||||
return new SlotId[](0);
|
||||
}
|
||||
bytes32[] memory slotIds =
|
||||
activeSlots.values(_toBytes32SetMapKey(requestId), msg.sender);
|
||||
return _toSlotIds(slotIds);
|
||||
bytes32[] memory result = new bytes32[](totalSlots);
|
||||
bytes32[] storage requestIds =
|
||||
activeHostRequests.getManyKeys(_toBytes32(msg.sender));
|
||||
console.log("[mySlots] total requestIds: ", requestIds.length);
|
||||
for (uint256 i = 0; i < requestIds.length; i++) {
|
||||
// There may exist slots that are still "active", but are part of a request
|
||||
// that is expired but has not been set to the cancelled state yet. In that
|
||||
// case, return an empty array.
|
||||
// TODO: remove cancelled lookups https://discord.com/channels/@me/958221374076366898/1044947242013954140
|
||||
bytes32 requestId = requestIds[i];
|
||||
if (_isCancelled(RequestId.wrap(requestId))) {
|
||||
continue;
|
||||
}
|
||||
if (activeRequestSlots.isOne(requestIds[i])) {
|
||||
bytes32[] storage slotIds = activeRequestSlots.getManyKeys(requestIds[i]);
|
||||
console.log("[mySlots] requestId: ");
|
||||
console.logBytes32(requestIds[i]);
|
||||
console.log("[mySlots] total slotsIds for requestId: ", slotIds.length);
|
||||
for (uint256 j = 0; j < slotIds.length; j++) {
|
||||
result[counter] = slotIds[j];
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return _toSlotIds(Utils._resize(result, counter));
|
||||
}
|
||||
|
||||
function _equals(RequestId a, RequestId b) internal pure returns (bool) {
|
||||
@ -111,8 +132,17 @@ contract Marketplace is Collateral, Proofs {
|
||||
context.endsAt = block.timestamp + request.ask.duration;
|
||||
_setProofEnd(_toEndId(id), context.endsAt);
|
||||
|
||||
activeRequestsForClients.add(_toAddressSetMapKey(request.client),
|
||||
RequestId.unwrap(id));
|
||||
bytes32 addrBytes32 = _toBytes32(request.client);
|
||||
if (!activeClientRequests.isOne(addrBytes32)) {
|
||||
activeClientRequests.createOne(addrBytes32);
|
||||
}
|
||||
if (!activeClientRequests.isMany(RequestId.unwrap(id))) {
|
||||
activeClientRequests.createMany(addrBytes32, RequestId.unwrap(id));
|
||||
}
|
||||
|
||||
if (!activeRequestSlots.isOne(RequestId.unwrap(id))) {
|
||||
activeRequestSlots.createOne(RequestId.unwrap(id));
|
||||
}
|
||||
|
||||
_createLock(_toLockId(id), request.expiry);
|
||||
|
||||
@ -148,11 +178,24 @@ contract Marketplace is Collateral, Proofs {
|
||||
slot.requestId = requestId;
|
||||
RequestContext storage context = _context(requestId);
|
||||
context.slotsFilled += 1;
|
||||
activeSlots.add(_toBytes32SetMapKey(requestId),
|
||||
slot.host,
|
||||
SlotId.unwrap(slotId));
|
||||
activeRequestsForHosts.add(_toBytes32AddressSetMapKey(requestId),
|
||||
slot.host);
|
||||
|
||||
console.log("FILLING slotId: ");
|
||||
console.logBytes32(SlotId.unwrap(slotId));
|
||||
|
||||
bytes32 sender = _toBytes32(msg.sender);
|
||||
if (!activeHostRequests.isOne(sender)) {
|
||||
activeHostRequests.createOne(sender);
|
||||
}
|
||||
// address => RequestId
|
||||
if (!activeHostRequests.isMany(RequestId.unwrap(requestId))) {
|
||||
activeHostRequests.createMany(sender, RequestId.unwrap(requestId));
|
||||
}
|
||||
|
||||
// RequestId => SlotId
|
||||
if (!activeRequestSlots.isMany(SlotId.unwrap(slotId))) {
|
||||
activeRequestSlots.createMany(RequestId.unwrap(requestId), SlotId.unwrap(slotId));
|
||||
}
|
||||
|
||||
emit SlotFilled(requestId, slotIndex, slotId);
|
||||
if (context.slotsFilled == request.ask.slots) {
|
||||
context.state = RequestState.Started;
|
||||
@ -179,13 +222,10 @@ contract Marketplace is Collateral, Proofs {
|
||||
|
||||
_unexpectProofs(_toProofId(slotId));
|
||||
|
||||
SetMap.Bytes32SetMapKey requestIdKey = _toBytes32SetMapKey(requestId);
|
||||
activeSlots.remove(requestIdKey,
|
||||
slot.host,
|
||||
SlotId.unwrap(slotId));
|
||||
if (activeSlots.length(requestIdKey, slot.host) == 0) {
|
||||
activeRequestsForHosts.remove(_toBytes32AddressSetMapKey(requestId),
|
||||
slot.host);
|
||||
console.log("FREEING slotId: ");
|
||||
console.logBytes32(SlotId.unwrap(slotId));
|
||||
if (activeRequestSlots.isMany(SlotId.unwrap(slotId))) {
|
||||
activeRequestSlots.deleteMany(SlotId.unwrap(slotId));
|
||||
}
|
||||
slot.host = address(0);
|
||||
slot.requestId = RequestId.wrap(0);
|
||||
@ -201,10 +241,11 @@ contract Marketplace is Collateral, Proofs {
|
||||
context.state = RequestState.Failed;
|
||||
_setProofEnd(_toEndId(requestId), block.timestamp - 1);
|
||||
context.endsAt = block.timestamp - 1;
|
||||
activeRequestsForClients.remove(_toAddressSetMapKey(request.client),
|
||||
RequestId.unwrap(requestId));
|
||||
activeRequestsForHosts.clear(_toBytes32AddressSetMapKey(requestId));
|
||||
activeSlots.clear(_toBytes32SetMapKey(requestId));
|
||||
console.log("about to delete many");
|
||||
activeClientRequests.deleteMany(RequestId.unwrap(requestId));
|
||||
console.log("about to clear all manys");
|
||||
activeRequestSlots.clearAllManys(RequestId.unwrap(requestId));
|
||||
console.log("about to cleared all manys");
|
||||
emit RequestFailed(requestId);
|
||||
|
||||
// TODO: burn all remaining slot collateral (note: slot collateral not
|
||||
@ -219,18 +260,19 @@ contract Marketplace is Collateral, Proofs {
|
||||
{
|
||||
require(_isFinished(requestId), "Contract not ended");
|
||||
RequestContext storage context = _context(requestId);
|
||||
Request storage request = _request(requestId);
|
||||
// Request storage request = _request(requestId);
|
||||
context.state = RequestState.Finished;
|
||||
activeRequestsForClients.remove(_toAddressSetMapKey(request.client),
|
||||
RequestId.unwrap(requestId));
|
||||
if (activeClientRequests.isMany(RequestId.unwrap(requestId))) {
|
||||
activeClientRequests.deleteMany(RequestId.unwrap(requestId));
|
||||
}
|
||||
SlotId slotId = _toSlotId(requestId, slotIndex);
|
||||
Slot storage slot = _slot(slotId);
|
||||
require(!slot.hostPaid, "Already paid");
|
||||
activeSlots.remove(_toBytes32SetMapKey(requestId),
|
||||
slot.host,
|
||||
SlotId.unwrap(slotId));
|
||||
activeRequestsForHosts.remove(_toBytes32AddressSetMapKey(requestId),
|
||||
slot.host);
|
||||
activeRequestSlots.deleteMany(SlotId.unwrap(slotId));
|
||||
if (activeRequestSlots.getManyCount() == 0) {
|
||||
activeRequestSlots.deleteOne(RequestId.unwrap(requestId));
|
||||
activeHostRequests.deleteMany(RequestId.unwrap(requestId));
|
||||
}
|
||||
uint256 amount = pricePerSlot(requests[requestId]);
|
||||
funds.sent += amount;
|
||||
funds.balance -= amount;
|
||||
@ -251,10 +293,10 @@ contract Marketplace is Collateral, Proofs {
|
||||
// Update request state to Cancelled. Handle in the withdraw transaction
|
||||
// as there needs to be someone to pay for the gas to update the state
|
||||
context.state = RequestState.Cancelled;
|
||||
activeRequestsForClients.remove(_toAddressSetMapKey(request.client),
|
||||
RequestId.unwrap(requestId));
|
||||
activeRequestsForHosts.clear(_toBytes32AddressSetMapKey(requestId));
|
||||
activeSlots.clear(_toBytes32SetMapKey(requestId));
|
||||
activeClientRequests.deleteMany(RequestId.unwrap(requestId));
|
||||
|
||||
activeRequestSlots.clearAllManys(RequestId.unwrap(requestId));
|
||||
// TODO: handle dangling RequestId in activeHostRequests (for address)
|
||||
emit RequestCancelled(requestId);
|
||||
|
||||
// TODO: To be changed once we start paying out hosts for the time they
|
||||
@ -454,6 +496,14 @@ contract Marketplace is Collateral, Proofs {
|
||||
}
|
||||
}
|
||||
|
||||
function _toBytes32(address addr)
|
||||
private
|
||||
pure
|
||||
returns (bytes32 result)
|
||||
{
|
||||
return bytes32(uint(uint160(addr)));
|
||||
}
|
||||
|
||||
function _toBytes32s(RequestId[] memory array)
|
||||
private
|
||||
pure
|
||||
|
||||
60
contracts/libs/Debug.sol
Normal file
60
contracts/libs/Debug.sol
Normal file
@ -0,0 +1,60 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.8;
|
||||
|
||||
import "./ORM2.sol";
|
||||
import "hardhat/console.sol"; // DELETE ME
|
||||
|
||||
library Debug {
|
||||
function _toHex16 (bytes16 data) private pure returns (bytes32 result) {
|
||||
result = bytes32 (data) & 0xFFFFFFFFFFFFFFFF000000000000000000000000000000000000000000000000 |
|
||||
(bytes32 (data) & 0x0000000000000000FFFFFFFFFFFFFFFF00000000000000000000000000000000) >> 64;
|
||||
result = result & 0xFFFFFFFF000000000000000000000000FFFFFFFF000000000000000000000000 |
|
||||
(result & 0x00000000FFFFFFFF000000000000000000000000FFFFFFFF0000000000000000) >> 32;
|
||||
result = result & 0xFFFF000000000000FFFF000000000000FFFF000000000000FFFF000000000000 |
|
||||
(result & 0x0000FFFF000000000000FFFF000000000000FFFF000000000000FFFF00000000) >> 16;
|
||||
result = result & 0xFF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 |
|
||||
(result & 0x00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000) >> 8;
|
||||
result = (result & 0xF000F000F000F000F000F000F000F000F000F000F000F000F000F000F000F000) >> 4 |
|
||||
(result & 0x0F000F000F000F000F000F000F000F000F000F000F000F000F000F000F000F00) >> 8;
|
||||
result = bytes32 (0x3030303030303030303030303030303030303030303030303030303030303030 +
|
||||
uint256 (result) +
|
||||
(uint256 (result) + 0x0606060606060606060606060606060606060606060606060606060606060606 >> 4 &
|
||||
0x0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F) * 7);
|
||||
}
|
||||
|
||||
function _toHex (bytes32 data) internal pure returns (string memory) {
|
||||
return string (abi.encodePacked ("0x", _toHex16 (bytes16 (data)), _toHex16 (bytes16 (data << 128))));
|
||||
}
|
||||
|
||||
// @notice Prints the contents of the one-to-many table
|
||||
// Usage example (from ORM): Debug._printTable(db._oneList,
|
||||
// getManyKeys(db, oneId),
|
||||
// getTotalManyCount(db));
|
||||
// @dev Explain to a developer any extra details
|
||||
// @param oneList list of one ids
|
||||
// @param manyKeys list of one ids
|
||||
// @param totalManyCount list of one ids
|
||||
// function _printTable(bytes32[] storage oneList,
|
||||
// bytes32[] storage manyKeys,
|
||||
// uint256 totalManyCount)
|
||||
function _printTable(ORM2.OneToMany storage db)
|
||||
internal
|
||||
view
|
||||
{
|
||||
console.log("|-----------------------------------------------------------------------------------------------------------------------------------------|");
|
||||
console.log("| Key | Value |");
|
||||
console.log("| ------------------------------------------------------------------ | ------------------------------------------------------------------ |");
|
||||
for(uint8 i = 0; i < db._oneList.length; i++) {
|
||||
bytes32 oneId = db._oneList[i];
|
||||
console.log("|", _toHex(oneId), "| |");
|
||||
|
||||
bytes32[] storage manyKeys = ORM2.getManyKeys(db, oneId);
|
||||
for(uint8 j = 0; j < manyKeys.length; j++) {
|
||||
bytes32 slotId = manyKeys[j];
|
||||
console.log("| |", _toHex(slotId), "|");
|
||||
}
|
||||
}
|
||||
console.log("|_________________________________________________________________________________________________________________________________________|");
|
||||
console.log(" TOTAL Values: ", ORM2.getTotalManyCount(db));
|
||||
}
|
||||
}
|
||||
233
contracts/libs/ORM2.sol
Normal file
233
contracts/libs/ORM2.sol
Normal file
@ -0,0 +1,233 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// heavily inspired by: https://bitbucket.org/rhitchens2/soliditystoragepatterns/src/master/GeneralizedCollection.sol
|
||||
pragma solidity ^0.8.8;
|
||||
|
||||
import "hardhat/console.sol";
|
||||
import "./Debug.sol"; // DELETE ME
|
||||
|
||||
library ORM2 {
|
||||
// first entity is called a "One"
|
||||
struct OneStruct {
|
||||
// needed to delete a "One"
|
||||
uint _oneListPointer;
|
||||
// One has many "Many"
|
||||
bytes32[] _manyIds;
|
||||
mapping(bytes32 => uint) _manyIdPointers; // manyId => row of _manyIds
|
||||
// more app data
|
||||
}
|
||||
|
||||
// other entity is called a "Many"
|
||||
struct ManyStruct {
|
||||
// needed to delete a "Many"
|
||||
uint _manyListPointer;
|
||||
// many has exactly one "One"
|
||||
bytes32 _oneId;
|
||||
// add app fields
|
||||
}
|
||||
|
||||
struct OneToMany {
|
||||
mapping(bytes32 => OneStruct) _oneStructs;
|
||||
bytes32[] _oneList;
|
||||
mapping(bytes32 => ManyStruct) _manyStructs;
|
||||
bytes32[] _manyList;
|
||||
}
|
||||
|
||||
function getOneCount(OneToMany storage db)
|
||||
internal
|
||||
view
|
||||
returns(uint)
|
||||
{
|
||||
return db._oneList.length;
|
||||
}
|
||||
|
||||
function getManyCount(OneToMany storage db) internal view returns(uint) {
|
||||
return db._manyList.length;
|
||||
}
|
||||
|
||||
function isOne(OneToMany storage db, bytes32 oneId)
|
||||
internal
|
||||
view
|
||||
returns(bool)
|
||||
{
|
||||
if(db._oneList.length == 0) return false;
|
||||
return db._oneList[db._oneStructs[oneId]._oneListPointer] == oneId;
|
||||
}
|
||||
|
||||
function isMany(OneToMany storage db, bytes32 manyId)
|
||||
internal
|
||||
view
|
||||
returns(bool)
|
||||
{
|
||||
if(db._manyList.length == 0) return false;
|
||||
uint256 row = db._manyStructs[manyId]._manyListPointer;
|
||||
bool retVal = db._manyList[row] == manyId;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
// Iterate over a One's Many keys
|
||||
function getManyCount(OneToMany storage db, bytes32 oneId)
|
||||
internal
|
||||
view
|
||||
returns(uint manyCount)
|
||||
{
|
||||
require(isOne(db, oneId), "oneId does not exist");
|
||||
return db._oneStructs[oneId]._manyIds.length;
|
||||
}
|
||||
|
||||
function getTotalManyCount(OneToMany storage db)
|
||||
internal
|
||||
view
|
||||
returns(uint manyCount)
|
||||
{
|
||||
return db._manyList.length;
|
||||
}
|
||||
|
||||
function getManyKeyAtIndex(OneToMany storage db,
|
||||
bytes32 oneId,
|
||||
uint row)
|
||||
internal
|
||||
view
|
||||
returns(bytes32 manyKey)
|
||||
{
|
||||
require(isOne(db, oneId), "oneId does not exist");
|
||||
return db._oneStructs[oneId]._manyIds[row];
|
||||
}
|
||||
|
||||
function getManyKeys(OneToMany storage db,
|
||||
bytes32 oneId)
|
||||
internal
|
||||
view
|
||||
returns(bytes32[] storage manyKeys)
|
||||
{
|
||||
require(isOne(db, oneId), "oneId does not exist");
|
||||
return db._oneStructs[oneId]._manyIds;
|
||||
}
|
||||
|
||||
// Insert
|
||||
function createOne(OneToMany storage db, bytes32 oneId)
|
||||
internal
|
||||
returns(bool)
|
||||
{
|
||||
require(!isOne(db, oneId), "oneId already exists"); // duplicate key prohibited
|
||||
|
||||
db._oneList.push(oneId);
|
||||
db._oneStructs[oneId]._oneListPointer = getOneCount(db) - 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
function createMany(OneToMany storage db, bytes32 oneId, bytes32 manyId)
|
||||
internal
|
||||
returns(bool)
|
||||
{
|
||||
require(isOne(db, oneId), "oneId does not exist");
|
||||
require(!isMany(db, manyId), "manyId already exists"); // duplicate key prohibited
|
||||
|
||||
ManyStruct storage manyRow = db._manyStructs[manyId];
|
||||
db._manyList.push(manyId);
|
||||
manyRow._manyListPointer = db._manyList.length - 1;
|
||||
manyRow._oneId = oneId; // each many has exactly one "One", so this is mandatory
|
||||
|
||||
// We also maintain a list of "Many" that refer to the "One", so ...
|
||||
OneStruct storage oneRow = db._oneStructs[oneId];
|
||||
oneRow._manyIds.push(manyId);
|
||||
oneRow._manyIdPointers[manyId] = oneRow._manyIds.length - 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Delete
|
||||
function deleteOne(OneToMany storage db, bytes32 oneId)
|
||||
internal
|
||||
returns(bool)
|
||||
{
|
||||
require(isOne(db, oneId), "oneId does not exist");
|
||||
require(db._oneStructs[oneId]._manyIds.length == 0, "references manys"); // this would break referential integrity
|
||||
|
||||
uint rowToDelete = db._oneStructs[oneId]._oneListPointer;
|
||||
bytes32 keyToMove = db._oneList[db._oneList.length-1];
|
||||
db._oneList[rowToDelete] = keyToMove;
|
||||
db._oneStructs[keyToMove]._oneListPointer = rowToDelete;
|
||||
db._oneList.pop();
|
||||
delete db._oneStructs[oneId];
|
||||
return true;
|
||||
}
|
||||
|
||||
function deleteMany(OneToMany storage db, bytes32 manyId)
|
||||
internal
|
||||
returns(bool)
|
||||
{
|
||||
require(isMany(db, manyId), "manys do not exist"); // non-existant key
|
||||
|
||||
console.log("deleting many, manyId: ");
|
||||
console.logBytes32(manyId);
|
||||
// delete from the Many table
|
||||
uint256 toDeleteIndex = db._manyStructs[manyId]._manyListPointer;
|
||||
|
||||
uint256 lastIndex = db._manyList.length - 1;
|
||||
|
||||
if (lastIndex != toDeleteIndex) {
|
||||
bytes32 lastValue = db._manyList[lastIndex];
|
||||
|
||||
// Move the last value to the index where the value to delete is
|
||||
db._manyList[toDeleteIndex] = lastValue;
|
||||
// Update the index for the moved value
|
||||
db._manyStructs[lastValue]._manyListPointer = toDeleteIndex; // Replace lastvalue's index to valueIndex
|
||||
}
|
||||
db._manyList.pop();
|
||||
|
||||
bytes32 oneId = db._manyStructs[manyId]._oneId;
|
||||
OneStruct storage oneRow = db._oneStructs[oneId];
|
||||
toDeleteIndex = oneRow._manyIdPointers[manyId];
|
||||
lastIndex = oneRow._manyIds.length - 1;
|
||||
if (lastIndex != toDeleteIndex) {
|
||||
bytes32 lastValue = oneRow._manyIds[lastIndex];
|
||||
|
||||
// Move the last value to the index where the value to delete is
|
||||
oneRow._manyIds[toDeleteIndex] = lastValue;
|
||||
// Update the index for the moved value
|
||||
oneRow._manyIdPointers[lastValue] = toDeleteIndex; // Replace lastvalue's index to valueIndex
|
||||
}
|
||||
oneRow._manyIds.pop();
|
||||
delete oneRow._manyIdPointers[manyId];
|
||||
|
||||
delete db._manyStructs[manyId];
|
||||
|
||||
|
||||
|
||||
// uint rowToDelete = db._manyStructs[manyId]._manyListPointer;
|
||||
// console.log("row to delete: ", rowToDelete);
|
||||
// bytes32 keyToMove = db._manyList[db._manyList.length-1];
|
||||
// db._manyList[rowToDelete] = keyToMove;
|
||||
// uint rowToMove = db._manyStructs[keyToMove]._manyListPointer;
|
||||
// db._manyStructs[manyId]._manyListPointer = rowToDelete;
|
||||
// db._manyStructs[keyToMove]._manyListPointer = rowToMove;
|
||||
// db._manyList.pop();
|
||||
|
||||
// we ALSO have to delete this key from the list in the ONE that was joined to this Many
|
||||
// bytes32 oneId = db._manyStructs[manyId]._oneId; // it's still there, just not dropped from index
|
||||
// rowToDelete = db._oneStructs[oneId]._manyIdPointers[manyId];
|
||||
// keyToMove = db._oneStructs[oneId]._manyIds[db._oneStructs[oneId]._manyIds.length-1];
|
||||
// db._oneStructs[oneId]._manyIds[rowToDelete] = keyToMove;
|
||||
// db._oneStructs[oneId]._manyIdPointers[keyToMove] = rowToDelete;
|
||||
// db._oneStructs[oneId]._manyIds.pop();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
function clearAllManys(OneToMany storage db, bytes32 oneId)
|
||||
internal
|
||||
returns(bool)
|
||||
{
|
||||
require(isOne(db, oneId), "oneId does not exist"); // non-existant key
|
||||
|
||||
console.log("[clearAllMany] clearing all slotIds for requestId: ", Debug._toHex(oneId));
|
||||
console.log("[clearAllMany] BEFORE clearing");
|
||||
Debug._printTable(db);
|
||||
// delete db._manyList;
|
||||
delete db._oneStructs[oneId]._manyIds;
|
||||
bool result = deleteOne(db, oneId);
|
||||
console.log("[clearAllMany] AFTER clearing");
|
||||
Debug._printTable(db);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@ -2,7 +2,7 @@
|
||||
pragma solidity ^0.8.8;
|
||||
|
||||
library Utils {
|
||||
function _resize(bytes32[] memory array, uint8 newSize)
|
||||
function _resize(bytes32[] memory array, uint256 newSize)
|
||||
internal
|
||||
pure
|
||||
returns (bytes32[] memory)
|
||||
|
||||
@ -691,7 +691,7 @@ describe("Marketplace", function () {
|
||||
})
|
||||
})
|
||||
|
||||
describe("list of active requests", function () {
|
||||
describe("list of active requests for client", function () {
|
||||
beforeEach(async function () {
|
||||
switchAccount(host)
|
||||
await token.approve(marketplace.address, collateral)
|
||||
@ -738,56 +738,6 @@ describe("Marketplace", function () {
|
||||
})
|
||||
})
|
||||
|
||||
describe("list of active requests for host", function () {
|
||||
beforeEach(async function () {
|
||||
switchAccount(client)
|
||||
await token.approve(marketplace.address, price(request))
|
||||
await marketplace.requestStorage(request)
|
||||
switchAccount(host)
|
||||
await token.approve(marketplace.address, collateral)
|
||||
await marketplace.deposit(collateral)
|
||||
})
|
||||
|
||||
it("is empty when no slot is filled", async function () {
|
||||
expect(await marketplace.requestsForHost(host.address)).to.deep.equal([])
|
||||
})
|
||||
|
||||
it("adds request to list when filling slot", async function () {
|
||||
await marketplace.fillSlot(slot.request, slot.index, proof)
|
||||
expect(await marketplace.requestsForHost(host.address)).to.deep.equal([
|
||||
slot.request,
|
||||
])
|
||||
})
|
||||
|
||||
it("removes request from list when cancelled", async function () {
|
||||
await marketplace.fillSlot(slot.request, slot.index, proof)
|
||||
await waitUntilCancelled(request)
|
||||
expect(await marketplace.requestsForHost(host.address)).to.deep.equal([])
|
||||
})
|
||||
|
||||
it("removes request from list when funds are withdrawn", async function () {
|
||||
await marketplace.fillSlot(slot.request, slot.index, proof)
|
||||
await waitUntilCancelled(request)
|
||||
switchAccount(client)
|
||||
await marketplace.withdrawFunds(slot.request)
|
||||
expect(await marketplace.requestsForHost(host.address)).to.deep.equal([])
|
||||
})
|
||||
|
||||
it("removes request from list when request fails", async function () {
|
||||
await waitUntilStarted(marketplace, request, proof)
|
||||
await waitUntilFailed(marketplace, request, slot)
|
||||
expect(await marketplace.requestsForHost(host.address)).to.deep.equal([])
|
||||
})
|
||||
|
||||
it("removes request from list when request finishes", async function () {
|
||||
switchAccount(host)
|
||||
await waitUntilStarted(marketplace, request, proof)
|
||||
await waitUntilFinished(marketplace, requestId(request))
|
||||
await marketplace.payoutSlot(slot.request, slot.index)
|
||||
expect(await marketplace.requestsForHost(host.address)).to.deep.equal([])
|
||||
})
|
||||
})
|
||||
|
||||
describe("list of active slots", function () {
|
||||
beforeEach(async function () {
|
||||
switchAccount(client)
|
||||
@ -802,7 +752,7 @@ describe("Marketplace", function () {
|
||||
await marketplace.fillSlot(slot.request, slot.index, proof)
|
||||
let slot1 = { ...slot, index: slot.index + 1 }
|
||||
await marketplace.fillSlot(slot.request, slot1.index, proof)
|
||||
expect(await marketplace.mySlots(slot.request)).to.deep.equal([
|
||||
expect(await marketplace.mySlots()).to.deep.equal([
|
||||
slotId(slot),
|
||||
slotId(slot1),
|
||||
])
|
||||
@ -813,9 +763,7 @@ describe("Marketplace", function () {
|
||||
let slot1 = { ...slot, index: slot.index + 1 }
|
||||
await marketplace.fillSlot(slot.request, slot1.index, proof)
|
||||
await marketplace.freeSlot(slotId(slot))
|
||||
expect(await marketplace.mySlots(slot.request)).to.deep.equal([
|
||||
slotId(slot1),
|
||||
])
|
||||
expect(await marketplace.mySlots()).to.deep.equal([slotId(slot1)])
|
||||
})
|
||||
|
||||
it("returns no slots when cancelled", async function () {
|
||||
@ -823,7 +771,7 @@ describe("Marketplace", function () {
|
||||
let slot1 = { ...slot, index: slot.index + 1 }
|
||||
await marketplace.fillSlot(slot.request, slot1.index, proof)
|
||||
await waitUntilCancelled(request)
|
||||
expect(await marketplace.mySlots(slot.request)).to.deep.equal([])
|
||||
expect(await marketplace.mySlots()).to.deep.equal([])
|
||||
})
|
||||
|
||||
it("removes active slots for all hosts in a request when it fails", async function () {
|
||||
@ -843,9 +791,8 @@ describe("Marketplace", function () {
|
||||
}
|
||||
|
||||
await waitUntilFailed(marketplace, request, slot)
|
||||
expect(await marketplace.mySlots(slot.request)).to.deep.equal([])
|
||||
switchAccount(host)
|
||||
expect(await marketplace.mySlots(slot.request)).to.deep.equal([])
|
||||
expect(await marketplace.mySlots()).to.deep.equal([])
|
||||
})
|
||||
|
||||
it("doesn't remove active slots for hosts in request that didn't fail", async function () {
|
||||
@ -864,6 +811,9 @@ describe("Marketplace", function () {
|
||||
// wait until first request fails
|
||||
await waitUntilFailed(marketplace, request, slot)
|
||||
|
||||
console.log("1st requestId: ", requestId(request))
|
||||
console.log("2nd requestId: ", requestId(request2))
|
||||
|
||||
// check that our active slots only contains slotIds from second request
|
||||
let expected = []
|
||||
let expectedSlot = { ...slot, index: 0, request: requestId(request2) }
|
||||
@ -872,19 +822,14 @@ describe("Marketplace", function () {
|
||||
let id = slotId(expectedSlot)
|
||||
expected.push(id)
|
||||
}
|
||||
expect(await marketplace.mySlots(slot.request)).to.deep.equal([])
|
||||
expect(await marketplace.mySlots(expectedSlot.request)).to.deep.equal(
|
||||
expected
|
||||
)
|
||||
expect(await marketplace.mySlots()).to.deep.equal(expected)
|
||||
})
|
||||
|
||||
it("removes slots from list when request finishes", async function () {
|
||||
await waitUntilStarted(marketplace, request, proof)
|
||||
await waitUntilFinished(marketplace, requestId(request))
|
||||
await marketplace.payoutSlot(slot.request, slot.index)
|
||||
expect(await marketplace.mySlots(slot.request)).to.not.contain(
|
||||
slotId(slot)
|
||||
)
|
||||
expect(await marketplace.mySlots()).to.not.contain(slotId(slot))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user