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
31 changes: 26 additions & 5 deletions src/facets/AllocateFacet.sol
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
// SPDX-License-Identifier: MIT
pragma solidity =0.8.25;

import {DataCapTypes} from "filecoin-solidity/types/DataCapTypes.sol";

Check warning on line 4 in src/facets/AllocateFacet.sol

View workflow job for this annotation

GitHub Actions / Solhint linter

Import in src/facets/AllocateFacet.sol doesn't exist in: filecoin-solidity/types/DataCapTypes.sol
import {DataCapAPI} from "filecoin-solidity/DataCapAPI.sol";

Check warning on line 5 in src/facets/AllocateFacet.sol

View workflow job for this annotation

GitHub Actions / Solhint linter

Import in src/facets/AllocateFacet.sol doesn't exist in: filecoin-solidity/DataCapAPI.sol
import {BigInts} from "filecoin-solidity/utils/BigInts.sol";

Check warning on line 6 in src/facets/AllocateFacet.sol

View workflow job for this annotation

GitHub Actions / Solhint linter

Import in src/facets/AllocateFacet.sol doesn't exist in: filecoin-solidity/utils/BigInts.sol
import {FilAddresses} from "filecoin-solidity/utils/FilAddresses.sol";

Check warning on line 7 in src/facets/AllocateFacet.sol

View workflow job for this annotation

GitHub Actions / Solhint linter

Import in src/facets/AllocateFacet.sol doesn't exist in: filecoin-solidity/utils/FilAddresses.sol
import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol";

Check warning on line 8 in src/facets/AllocateFacet.sol

View workflow job for this annotation

GitHub Actions / Solhint linter

Import in src/facets/AllocateFacet.sol doesn't exist in: @openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol

import {IFacet} from "../interfaces/IFacet.sol";
import {Modifiers} from "../Modifiers.sol";
Expand Down Expand Up @@ -49,12 +49,16 @@
if (allocReq.length * replicaSize < appConfig.minRequiredStorageProviders) {
revert ErrorLib.NotEnoughAllocationData();
}
uint256 requiredCollateral = appConfig.collateralPerCID * allocReq.length * replicaSize;
if (msg.value < requiredCollateral) {
revert ErrorLib.InsufficientCollateral(requiredCollateral);
{
uint256 requiredCollateral = appConfig.collateralPerCID * allocReq.length * replicaSize;
if (msg.value < requiredCollateral) {
revert ErrorLib.InsufficientCollateral(requiredCollateral);
}
}

uint64[] memory providers = StorageEntityPicker._pickStorageProviders(appConfig.minRequiredStorageProviders);
uint256 maxSpacePerProvider =
_calculateMaxAllocationSizePerProvider(allocReq, replicaSize, appConfig.minRequiredStorageProviders);
uint64[] memory providers =
StorageEntityPicker._pickStorageProviders(appConfig.minRequiredStorageProviders, maxSpacePerProvider);

int64 termMin = FilecoinEpochCalculator.getTermMin();
int64 termMax = FilecoinEpochCalculator.calcTermMax();
Expand Down Expand Up @@ -99,4 +103,21 @@

return packageId;
}

/**
* @dev Calculate the worst case (maximum) allocation size based on the request.
* cost: approx 20 gas per allocReq entry.
*/
function _calculateMaxAllocationSizePerProvider(
Types.AllocationRequest[] calldata allocReq,
uint256 replicaSize,
uint256 minProviders
) internal pure returns (uint256) {
uint256 totalBytes = 0;
for (uint256 i = 0; i < allocReq.length; i++) {
totalBytes += uint256(allocReq[i].size) * replicaSize;
}
// ceil(a/b) == (a + b - 1) / b
return (totalBytes + minProviders - 1) / minProviders;
}
}
62 changes: 42 additions & 20 deletions src/facets/StorageEntityManagerFacet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,13 @@ import {Events} from "../libraries/Events.sol";
contract StorageEntityManagerFacet is IFacet, Modifiers {
// get the function selectors for this facet for deployment and update scripts
function selectors() external pure returns (bytes4[] memory selectors_) {
selectors_ = new bytes4[](7);
selectors_ = new bytes4[](6);
selectors_[0] = this.createStorageEntity.selector;
selectors_[1] = this.addStorageProviders.selector;
selectors_[2] = this.removeStorageProviders.selector;
selectors_[3] = this.changeStorageEntityActiveStatus.selector;
selectors_[4] = this.getStorageEntity.selector;
selectors_[5] = this.isStorageProviderUsed.selector;
selectors_[6] = this.getStorageEntities.selector;
selectors_[3] = this.setStorageEntityActiveStatus.selector;
selectors_[4] = this.isStorageProviderUsed.selector;
selectors_[5] = this.setStorageProviderDetails.selector;
}

function createStorageEntity(address entityOwner, uint64[] calldata storageProviders)
Expand Down Expand Up @@ -63,7 +62,7 @@ contract StorageEntityManagerFacet is IFacet, Modifiers {

Storage.StorageEntity storage se = Storage.s().storageEntities[entityOwner];

_ensureStorageEntityNotExists(se);
_ensureStorageEntityExists(se);

for (uint256 i = 0; i < storageProviders.length; i++) {
se.storageProviders.push(storageProviders[i]);
Expand All @@ -79,16 +78,21 @@ contract StorageEntityManagerFacet is IFacet, Modifiers {
{
Storage.StorageEntity storage se = Storage.s().storageEntities[entityOwner];

_ensureStorageEntityNotExists(se);
_ensureStorageEntityExists(se);

for (uint256 j = 0; j < storageProviders.length; j++) {
uint64 sp = storageProviders[j];

_ensureStorageProviderIsAssignedToStorageEntity(se, sp);

Storage.s().usedStorageProviders[sp] = false;
for (uint256 i = 0; i < se.storageProviders.length; i++) {
if (se.storageProviders[i] == sp) {
se.storageProviders[i] = se.storageProviders[se.storageProviders.length - 1];
se.storageProviders.pop();
Storage.s().usedStorageProviders[sp] = false;

se.providerDetails[sp] = Storage.ProviderDetails({isActive: false, spaceLeft: 0});
break;
}
}
Expand All @@ -97,13 +101,13 @@ contract StorageEntityManagerFacet is IFacet, Modifiers {
emit Events.StorageProviderRemoved(msg.sender, entityOwner, storageProviders);
}

function changeStorageEntityActiveStatus(address entityOwner, bool isActive)
function setStorageEntityActiveStatus(address entityOwner, bool isActive)
external
onlyOwnerOrStorageEntity(entityOwner)
{
Storage.StorageEntity storage se = Storage.s().storageEntities[entityOwner];

_ensureStorageEntityNotExists(se);
_ensureStorageEntityExists(se);

se.isActive = isActive;

Expand All @@ -118,26 +122,44 @@ contract StorageEntityManagerFacet is IFacet, Modifiers {
}
}

function _ensureStorageEntityNotExists(Storage.StorageEntity storage se) internal view {
function _ensureStorageEntityExists(Storage.StorageEntity storage se) internal view {
if (se.owner == address(0)) {
revert ErrorLib.StorageEntityDoesNotExist();
}
}

function getStorageEntity(address entityOwner) external view returns (Storage.StorageEntity memory) {
return Storage.s().storageEntities[entityOwner];
}

function isStorageProviderUsed(uint64 storageProvider) external view returns (bool) {
return Storage.s().usedStorageProviders[storageProvider];
}

function getStorageEntities() external view returns (Storage.StorageEntity[] memory) {
address[] storage entityAddresses = Storage.s().entityAddresses;
Storage.StorageEntity[] memory storageEntities = new Storage.StorageEntity[](entityAddresses.length);
for (uint256 i = 0; i < entityAddresses.length; i++) {
storageEntities[i] = Storage.s().storageEntities[entityAddresses[i]];
function setStorageProviderDetails(
address entityOwner,
uint64 storageProvider,
Storage.ProviderDetails calldata details
) external onlyOwnerOrStorageEntity(entityOwner) {
Storage.StorageEntity storage se = Storage.s().storageEntities[entityOwner];

_ensureStorageEntityExists(se);

_ensureStorageProviderIsAssignedToStorageEntity(se, storageProvider);

se.providerDetails[storageProvider] = details;
}

function _ensureStorageProviderIsAssignedToStorageEntity(Storage.StorageEntity storage se, uint64 storageProvider)
internal
view
{
// Check if sp is assigned to the entity
bool isAssigned = false;
for (uint256 i = 0; i < se.storageProviders.length; i++) {
if (se.storageProviders[i] == storageProvider) {
isAssigned = true;
break;
}
}
if (!isAssigned) {
revert ErrorLib.StorageProviderNotAssignedToEntity();
}
return storageEntities;
}
}
41 changes: 40 additions & 1 deletion src/facets/ViewFacet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@ import {FilecoinConverter} from "../libraries/FilecoinConverter.sol";
contract ViewFacet is IFacet {
// get the function selectors for this facet for deployment and update scripts
function selectors() external pure returns (bytes4[] memory selectors_) {
selectors_ = new bytes4[](5);
selectors_ = new bytes4[](7);
selectors_[0] = this.getAllocationPackage.selector;
selectors_[1] = this.getClientPackagesWithClaimStatus.selector;
selectors_[2] = this.getPackageWithClaimStatus.selector;
selectors_[3] = this.checkProviderClaims.selector;
selectors_[4] = this.getAppConfig.selector;
selectors_[5] = this.getStorageEntity.selector;
selectors_[6] = this.getStorageEntities.selector;
}

function getAppConfig() external view returns (Storage.AppConfig memory appConfig) {
Expand Down Expand Up @@ -163,4 +165,41 @@ contract ViewFacet is IFacet {

return result.batch_info.success_count == allocationIds.length;
}

function getStorageEntity(address entityOwner) external view returns (Types.StorageEntityView memory) {
if (Storage.s().storageEntities[entityOwner].owner == address(0)) {
revert ErrorLib.StorageEntityDoesNotExist();
}
return _storageEntityToView(Storage.s().storageEntities[entityOwner]);
}

function getStorageEntities() external view returns (Types.StorageEntityView[] memory) {
address[] storage entityAddresses = Storage.s().entityAddresses;
Types.StorageEntityView[] memory storageEntities = new Types.StorageEntityView[](entityAddresses.length);
for (uint256 i = 0; i < entityAddresses.length; i++) {
storageEntities[i] = _storageEntityToView(Storage.s().storageEntities[entityAddresses[i]]);
}
return storageEntities;
}

function _storageEntityToView(Storage.StorageEntity storage se)
internal
view
returns (Types.StorageEntityView memory)
{
Types.StorageEntityView memory entityView;
entityView.isActive = se.isActive;
entityView.owner = se.owner;
entityView.storageProviders = se.storageProviders;
entityView.providerDetails = new Types.ProviderDetailsView[](se.storageProviders.length);
for (uint256 i = 0; i < se.storageProviders.length; i++) {
uint64 providerId = se.storageProviders[i];
entityView.providerDetails[i] = Types.ProviderDetailsView({
providerId: providerId,
spaceLeft: se.providerDetails[providerId].spaceLeft,
isActive: se.providerDetails[providerId].isActive
});
}
return entityView;
}
}
3 changes: 3 additions & 0 deletions src/libraries/Errors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -119,4 +119,7 @@ library ErrorLib {

// 0x35278d12
error Overflow();

// 0x8865bd93
error StorageProviderNotAssignedToEntity();
}
6 changes: 6 additions & 0 deletions src/libraries/Storage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,16 @@ library Storage {
uint256 collateral; // Collateral amount
}

struct ProviderDetails {
bool isActive; // Whether the provider is active
uint256 spaceLeft; // Space left for the provider
}

struct StorageEntity {
bool isActive; // Whether the storage entity is active
address owner; // Owner address, used to verify ownership
uint64[] storageProviders; // List of storage providers
mapping(uint64 => ProviderDetails) providerDetails; // Mapping by provider ID
}

/**
Expand Down
28 changes: 23 additions & 5 deletions src/libraries/StorageEntityPicker.sol
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,10 @@ library StorageEntityPicker {
* @param numEntities The number of storage providers to pick
*/
// slither-disable-next-line weak-prng
function _pickStorageProviders(uint256 numEntities) internal returns (uint64[] memory) {
function _pickStorageProviders(uint256 numEntities, uint256 maxSpacePerProvider)
internal
returns (uint64[] memory)
{
address[] storage entityAddresses = Storage.s().entityAddresses;
uint256 entityLength = entityAddresses.length;

Expand All @@ -59,10 +62,25 @@ library StorageEntityPicker {
address entityAddress = entityAddresses[index];

Storage.StorageEntity storage se = Storage.s().storageEntities[entityAddress];
// SE is active, and has at least one storage provider
if (se.isActive && se.storageProviders.length > 0) {
storageProviders[pickedCount] = Storage.s().storageEntities[entityAddress].storageProviders[0];
pickedCount++;

if (!se.isActive || se.storageProviders.length == 0) {
// If the SE is not active
// or doesn't have at least one provider
// then we can skip it
continue;
}
// Select one storage provider from the active storage entity that has enough space left
for (uint256 j = 0; j < se.storageProviders.length; j++) {
uint64 provider = se.storageProviders[j];

if (
se.providerDetails[provider].isActive
&& se.providerDetails[provider].spaceLeft >= maxSpacePerProvider
) {
storageProviders[pickedCount] = provider;
pickedCount++;
break; // Break the loop after picking one provider
}
}
}

Expand Down
13 changes: 13 additions & 0 deletions src/libraries/Types.sol
Original file line number Diff line number Diff line change
Expand Up @@ -132,4 +132,17 @@ library Types {
// 15301680 -> 1A 00E9A4A0 -> 1 + 4 bytes
int64 expiration;
}

struct StorageEntityView {
bool isActive;
address owner;
uint64[] storageProviders;
ProviderDetailsView[] providerDetails;
}

struct ProviderDetailsView {
bool isActive; // Whether the provider is active
uint64 providerId; // Provider ID
uint256 spaceLeft; // Space left for the provider
}
}
Loading