Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions abis/PoRepMarket.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,19 @@
],
"stateMutability": "view"
},
{
"type": "function",
"name": "MAX_DEAL_DURATION_DAYS",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint32",
"internalType": "uint32"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "UPGRADER_ROLE",
Expand Down Expand Up @@ -1063,6 +1076,11 @@
"name": "InvalidClientSmartContractAddress",
"inputs": []
},
{
"type": "error",
"name": "InvalidDealDuration",
"inputs": []
},
{
"type": "error",
"name": "InvalidIndexingPct",
Expand Down
68 changes: 55 additions & 13 deletions src/PoRepMarket.sol
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,15 @@ contract PoRepMarket is Initializable, AccessControlUpgradeable, UUPSUpgradeable
*/
bytes32 public constant UPGRADER_ROLE = keccak256("UPGRADER_ROLE");

/**
* @notice Maximum Filecoin storage deal duration: 1278 days (~3.5 years),
* per FIP-0052 (NV21 actor policy update).
* References:
* https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0052.md
* https://github.com/filecoin-project/core-devs/blob/master/Network%20Upgrades/v21.md
*/
uint32 public constant MAX_DEAL_DURATION_DAYS = 1278;

/// @custom:storage-location erc7201:porepmarket.storage.DealProposalsStorage
struct DealProposalsStorage {
mapping(uint256 dealId => PoRepTypes.DealProposal) _dealProposals;
Expand Down Expand Up @@ -154,6 +163,7 @@ contract PoRepMarket is Initializable, AccessControlUpgradeable, UUPSUpgradeable
error EmptyManifestLocation();
error TooLongManifestLocation();
error InvalidClientSmartContractAddress();
error InvalidDealDuration();

/**
* @notice Constructor
Expand Down Expand Up @@ -201,19 +211,9 @@ contract PoRepMarket is Initializable, AccessControlUpgradeable, UUPSUpgradeable
SLITypes.DealTerms calldata terms,
string calldata manifestLocation
) external {
if (requirements.retrievabilityBps > 10_000) {
revert InvalidRetrievabilityBps(requirements.retrievabilityBps);
}
if (requirements.indexingPct > 100) {
revert InvalidIndexingPct(requirements.indexingPct);
}

if (bytes(manifestLocation).length == 0) {
revert EmptyManifestLocation();
}
if (bytes(manifestLocation).length > 2048) {
revert TooLongManifestLocation();
}
_ensureCorrectManifestLocation(manifestLocation);
_ensureCorrectRequirements(requirements);
_ensureCorrectTerms(terms);

DealProposalsStorage storage $ = s();

Expand Down Expand Up @@ -474,6 +474,48 @@ contract PoRepMarket is Initializable, AccessControlUpgradeable, UUPSUpgradeable
if (dp.state != expectedState) revert DealNotInExpectedState(dp.dealId, dp.state, expectedState);
}

/**
* @notice Ensures the requirements are correct
* @param requirements The SLI thresholds for the deal
*/
function _ensureCorrectRequirements(SLITypes.SLIThresholds calldata requirements) internal pure {
if (requirements.retrievabilityBps > 10_000) {
revert InvalidRetrievabilityBps(requirements.retrievabilityBps);
}
if (requirements.indexingPct > 100) {
revert InvalidIndexingPct(requirements.indexingPct);
}
}

/**
* @notice Ensures the terms are correct
* @param terms The terms for the deal
*/
function _ensureCorrectTerms(SLITypes.DealTerms calldata terms) internal pure {
if (terms.durationDays == 0) {
revert InvalidDealDuration();
}
if (terms.durationDays > MAX_DEAL_DURATION_DAYS) {
revert InvalidDealDuration();
}
if (terms.durationDays % 30 != 0) {
revert InvalidDealDuration();
}
}

/**
* @notice Ensures the manifest location is correct
* @param manifestLocation The manifest location for the deal
*/
function _ensureCorrectManifestLocation(string calldata manifestLocation) internal pure {
if (bytes(manifestLocation).length == 0) {
revert EmptyManifestLocation();
}
if (bytes(manifestLocation).length > 2048) {
revert TooLongManifestLocation();
}
}

// solhint-disable no-empty-blocks
/**
* @notice Authorizes an upgrade
Expand Down
27 changes: 26 additions & 1 deletion test/PoRepMarket.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ contract PoRepMarketTest is Test {
SLITypes.SLIThresholds({retrievabilityBps: 80, bandwidthMbps: 500, latencyMs: 200, indexingPct: 90});

SLITypes.DealTerms internal defaultTerms =
SLITypes.DealTerms({dealSizeBytes: 1024, pricePerSector: 100, durationDays: 365});
SLITypes.DealTerms({dealSizeBytes: 1024, pricePerSector: 100, durationDays: 360});

string public expectedManifestLocation = "https://example.com/manifest";

Expand Down Expand Up @@ -556,6 +556,31 @@ contract PoRepMarketTest is Test {
poRepMarket.updateManifestLocation(dealId, "");
}

function testProposeDealRevertsWhenDealDurationIsZero() public {
SLITypes.DealTerms memory badTerms =
SLITypes.DealTerms({durationDays: 0, dealSizeBytes: 1024, pricePerSector: 100});
vm.prank(clientAddress);
vm.expectRevert(abi.encodeWithSelector(PoRepMarket.InvalidDealDuration.selector));
poRepMarket.proposeDeal(defaultRequirements, badTerms, expectedManifestLocation);
}

function testProposeDealRevertsWhenDealDurationIsNotMultiplicatioveOf30() public {
SLITypes.DealTerms memory badTerms =
SLITypes.DealTerms({durationDays: 31, dealSizeBytes: 1024, pricePerSector: 100});
vm.prank(clientAddress);
vm.expectRevert(abi.encodeWithSelector(PoRepMarket.InvalidDealDuration.selector));
poRepMarket.proposeDeal(defaultRequirements, badTerms, expectedManifestLocation);
}

function testProposeDealRevertsWhenDealDurationExceedsMaximum() public {
SLITypes.DealTerms memory badTerms = SLITypes.DealTerms({
durationDays: poRepMarket.MAX_DEAL_DURATION_DAYS() + 12, dealSizeBytes: 1024, pricePerSector: 100
});
vm.prank(clientAddress);
vm.expectRevert(abi.encodeWithSelector(PoRepMarket.InvalidDealDuration.selector));
poRepMarket.proposeDeal(defaultRequirements, badTerms, expectedManifestLocation);
}

function testUpdateManifestLocationRevertsUnauthorisedCaller() public {
vm.prank(clientAddress);
poRepMarket.proposeDeal(defaultRequirements, defaultTerms, expectedManifestLocation);
Expand Down
Loading