diff --git a/contracts/Configuration.sol b/contracts/Configuration.sol index 861a144..b1c539d 100644 --- a/contracts/Configuration.sol +++ b/contracts/Configuration.sol @@ -6,6 +6,7 @@ import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; struct MarketplaceConfig { CollateralConfig collateral; ProofConfig proofs; + SlotReservationsConfig reservations; } struct CollateralConfig { @@ -29,3 +30,10 @@ struct ProofConfig { // blocks. Should be a prime number to ensure there are no cycles. uint8 downtimeProduct; } + +struct SlotReservationsConfig { + // Percentage of total time before expiry that all addresses are eligible to + // reserve a slot. Total time is the duration between request creation and + // expiry. Valid range is [0, 99]. + uint8 saturation; +} diff --git a/contracts/FuzzMarketplace.sol b/contracts/FuzzMarketplace.sol index efdd902..d7c20a3 100644 --- a/contracts/FuzzMarketplace.sol +++ b/contracts/FuzzMarketplace.sol @@ -10,7 +10,8 @@ contract FuzzMarketplace is Marketplace { Marketplace( MarketplaceConfig( CollateralConfig(10, 5, 3, 10), - ProofConfig(10, 5, 64, "", 67) + ProofConfig(10, 5, 64, "", 67), + SlotReservationsConfig(20) ), new TestToken(), new TestVerifier() diff --git a/contracts/Marketplace.sol b/contracts/Marketplace.sol index 43bcad8..e5eebdc 100644 --- a/contracts/Marketplace.sol +++ b/contracts/Marketplace.sol @@ -59,7 +59,10 @@ contract Marketplace is SlotReservations, Proofs, StateRetrieval, Endian { MarketplaceConfig memory configuration, IERC20 token_, IGroth16Verifier verifier - ) Proofs(configuration.proofs, verifier) { + ) + SlotReservations(configuration.reservations) + Proofs(configuration.proofs, verifier) + { _token = token_; require( diff --git a/contracts/Requests.sol b/contracts/Requests.sol index 2a79235..c8033d9 100644 --- a/contracts/Requests.sol +++ b/contracts/Requests.sol @@ -10,7 +10,10 @@ struct Request { Content content; uint256 expiry; // amount of seconds since start of the request at which this request expires bytes32 nonce; // random nonce to differentiate between similar requests - uint8 expansion; // Percentage of addresses eligible to reserve a slot halfway between request creation and when all network addresses are eligible to participate. [1-100]. + // Percentage of addresses eligible to reserve a slot halfway between request + // creation and when all network addresses are eligible to participate + // (saturation). Acceptable range is [1-100]. + uint8 expansion; } struct Ask { diff --git a/contracts/SlotReservations.sol b/contracts/SlotReservations.sol index 5ab0439..641bf6d 100644 --- a/contracts/SlotReservations.sol +++ b/contracts/SlotReservations.sol @@ -2,15 +2,22 @@ pragma solidity 0.8.23; import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; +import "./Configuration.sol"; import "./Requests.sol"; contract SlotReservations { using EnumerableSet for EnumerableSet.AddressSet; mapping(SlotId => EnumerableSet.AddressSet) internal _reservations; + SlotReservationsConfig private _config; uint8 private constant _MAX_RESERVATIONS = 3; + constructor(SlotReservationsConfig memory config) { + require(config.saturation <= 100, "saturation must be [0, 100]"); + _config = config; + } + function reserveSlot(SlotId slotId) public { address host = msg.sender; require(canReserveSlot(slotId), "Reservation not allowed"); diff --git a/contracts/TestSlotReservations.sol b/contracts/TestSlotReservations.sol index edc751e..31d19d6 100644 --- a/contracts/TestSlotReservations.sol +++ b/contracts/TestSlotReservations.sol @@ -6,6 +6,9 @@ import "./SlotReservations.sol"; contract TestSlotReservations is SlotReservations { using EnumerableSet for EnumerableSet.AddressSet; + // solhint-disable-next-line no-empty-blocks + constructor(SlotReservationsConfig memory config) SlotReservations(config) {} + function contains(SlotId slotId, address host) public view returns (bool) { return _reservations[slotId].contains(host); } diff --git a/deploy/marketplace.js b/deploy/marketplace.js index e4bc570..044bf75 100644 --- a/deploy/marketplace.js +++ b/deploy/marketplace.js @@ -14,7 +14,10 @@ const CONFIGURATION = { // `downtime` needs to be larger than `period` when running hardhat // in automine mode, because it can produce a block every second downtime: 64, - downtimeProduct: 67 + downtimeProduct: 67, + // saturate addresses eligible to reserve slots 20% of the total time before + // expiry + saturation: 20 }, } diff --git a/test/SlotReservations.test.js b/test/SlotReservations.test.js index a45700c..ecab002 100644 --- a/test/SlotReservations.test.js +++ b/test/SlotReservations.test.js @@ -4,6 +4,7 @@ const { exampleRequest } = require("./examples") const { requestId, slotId } = require("./ids") describe("SlotReservations", function () { + const saturation = 20 let reservations let provider, address1, address2, address3 let request @@ -14,7 +15,7 @@ describe("SlotReservations", function () { let SlotReservations = await ethers.getContractFactory( "TestSlotReservations" ) - reservations = await SlotReservations.deploy() + reservations = await SlotReservations.deploy({ saturation }) ;[provider, address1, address2, address3] = await ethers.getSigners() request = await exampleRequest() diff --git a/test/examples.js b/test/examples.js index 78960ca..d30d6b5 100644 --- a/test/examples.js +++ b/test/examples.js @@ -16,6 +16,9 @@ const exampleConfiguration = () => ({ zkeyHash: "", downtimeProduct: 67, }, + reservations: { + saturation: 20, + }, }) const exampleRequest = async () => { diff --git a/test/ids.js b/test/ids.js index c1efcac..142c4ea 100644 --- a/test/ids.js +++ b/test/ids.js @@ -2,7 +2,8 @@ const { ethers } = require("hardhat") const { keccak256, defaultAbiCoder } = ethers.utils function requestId(request) { - const Ask = "tuple(int64, uint256, uint256, uint256, uint256, uint256, uint64)" + const Ask = + "tuple(int64, uint256, uint256, uint256, uint256, uint256, uint64)" const Content = "tuple(string, bytes32)" const Request = "tuple(address, " + Ask + ", " + Content + ", uint256, bytes32, uint8)" @@ -33,7 +34,7 @@ function requestToArray(request) { contentToArray(request.content), request.expiry, request.nonce, - request.expansion + request.expansion, ], ] }