Skip to content
Closed
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
10 changes: 10 additions & 0 deletions service_contracts/abi/ServiceProviderRegistry.abi.json
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,11 @@
"name": "paymentTokenAddress",
"type": "address",
"internalType": "contract IERC20"
},
{
"name": "ipniPeerId",
"type": "bytes",
"internalType": "bytes"
}
]
},
Expand Down Expand Up @@ -1252,6 +1257,11 @@
"name": "paymentTokenAddress",
"type": "address",
"internalType": "contract IERC20"
},
{
"name": "ipniPeerId",
"type": "bytes",
"internalType": "bytes"
}
]
},
Expand Down
4 changes: 4 additions & 0 deletions service_contracts/src/ServiceProviderRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ contract ServiceProviderRegistry is
/// @notice Maximum length for location field
uint256 private constant MAX_LOCATION_LENGTH = 128;

/// @notice Maximum length for IPNI peer ID
uint256 private constant MAX_IPNI_PEER_ID_LENGTH = 128;

/// @notice Burn actor address for burning FIL
address public constant BURN_ACTOR = 0xff00000000000000000000000000000000000063;

Expand Down Expand Up @@ -821,6 +824,7 @@ contract ServiceProviderRegistry is
require(pdpOffering.minProvingPeriodInEpochs > 0, "Min proving period must be greater than 0");
require(bytes(pdpOffering.location).length > 0, "Location cannot be empty");
require(bytes(pdpOffering.location).length <= MAX_LOCATION_LENGTH, "Location too long");
require(pdpOffering.ipniPeerId.length <= MAX_IPNI_PEER_ID_LENGTH, "IPNI peer ID too long");
}

/// @notice Validate capability key-value pairs
Expand Down
1 change: 1 addition & 0 deletions service_contracts/src/ServiceProviderRegistryStorage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ contract ServiceProviderRegistryStorage {
uint256 minProvingPeriodInEpochs; // Minimum proving period in epochs
string location; // Geographic location of the service provider
IERC20 paymentTokenAddress; // Token contract for payment (IERC20(address(0)) for FIL)
bytes ipniPeerId; // IPNI peer ID (max 128 bytes, can be empty)
}

/// @notice Combined provider and product information for detailed queries
Expand Down
12 changes: 8 additions & 4 deletions service_contracts/test/FilecoinWarmStorageService.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,8 @@ contract FilecoinWarmStorageServiceTest is Test {
storagePricePerTibPerMonth: 1 ether,
minProvingPeriodInEpochs: 2880,
location: "US-Central",
paymentTokenAddress: IERC20(address(0)) // Payment in FIL
paymentTokenAddress: IERC20(address(0)), // Payment in FIL
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

make this a constant like

IERC20 constant NATIVE_FIL = IERC20(address(0))

can declare it outside of the test contract

ipniPeerId: hex""
})
),
new string[](0),
Expand All @@ -172,7 +173,8 @@ contract FilecoinWarmStorageServiceTest is Test {
storagePricePerTibPerMonth: 1 ether,
minProvingPeriodInEpochs: 2880,
location: "US-Central",
paymentTokenAddress: IERC20(address(0)) // Payment in FIL
paymentTokenAddress: IERC20(address(0)), // Payment in FIL
ipniPeerId: hex""
})
),
new string[](0),
Expand All @@ -195,7 +197,8 @@ contract FilecoinWarmStorageServiceTest is Test {
storagePricePerTibPerMonth: 1 ether,
minProvingPeriodInEpochs: 2880,
location: "US-Central",
paymentTokenAddress: IERC20(address(0)) // Payment in FIL
paymentTokenAddress: IERC20(address(0)), // Payment in FIL
ipniPeerId: hex""
})
),
new string[](0),
Expand All @@ -218,7 +221,8 @@ contract FilecoinWarmStorageServiceTest is Test {
storagePricePerTibPerMonth: 1 ether,
minProvingPeriodInEpochs: 2880,
location: "US-Central",
paymentTokenAddress: IERC20(address(0)) // Payment in FIL
paymentTokenAddress: IERC20(address(0)), // Payment in FIL
ipniPeerId: hex""
})
),
new string[](0),
Expand Down
3 changes: 2 additions & 1 deletion service_contracts/test/FilecoinWarmStorageServiceOwner.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,8 @@ contract FilecoinWarmStorageServiceOwnerTest is Test {
storagePricePerTibPerMonth: 5 * 10 ** 6, // 5 USDFC per TiB per month
minProvingPeriodInEpochs: 2880,
location: "US",
paymentTokenAddress: IERC20(address(0))
paymentTokenAddress: IERC20(address(0)),
ipniPeerId: hex""
})
),
capabilityKeys,
Expand Down
6 changes: 4 additions & 2 deletions service_contracts/test/ProviderValidation.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,8 @@ contract ProviderValidationTest is Test {
storagePricePerTibPerMonth: 1 ether,
minProvingPeriodInEpochs: 2880,
location: "US-West",
paymentTokenAddress: IERC20(address(0)) // Payment in FIL
paymentTokenAddress: IERC20(address(0)), // Payment in FIL
ipniPeerId: hex""
})
),
new string[](0),
Expand Down Expand Up @@ -163,7 +164,8 @@ contract ProviderValidationTest is Test {
storagePricePerTibPerMonth: 1 ether,
minProvingPeriodInEpochs: 2880,
location: "US-West",
paymentTokenAddress: IERC20(address(0)) // Payment in FIL
paymentTokenAddress: IERC20(address(0)), // Payment in FIL
ipniPeerId: hex""
})
),
new string[](0),
Expand Down
21 changes: 14 additions & 7 deletions service_contracts/test/ServiceProviderRegistry.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ contract ServiceProviderRegistryTest is Test {
storagePricePerTibPerMonth: 500000000000000000, // 0.5 FIL per TiB per month
minProvingPeriodInEpochs: 2880,
location: "US-East",
paymentTokenAddress: IERC20(address(0)) // Payment in FIL
paymentTokenAddress: IERC20(address(0)), // Payment in FIL
ipniPeerId: hex""
});

// Encode PDP data
Expand Down Expand Up @@ -111,7 +112,8 @@ contract ServiceProviderRegistryTest is Test {
storagePricePerTibPerMonth: 500000000000000000, // 0.5 FIL per TiB per month
minProvingPeriodInEpochs: 2880,
location: "US-East",
paymentTokenAddress: IERC20(address(0)) // Payment in FIL
paymentTokenAddress: IERC20(address(0)), // Payment in FIL
ipniPeerId: hex""
});

// Encode PDP data
Expand Down Expand Up @@ -182,7 +184,8 @@ contract ServiceProviderRegistryTest is Test {
storagePricePerTibPerMonth: 500000000000000000, // 0.5 FIL per TiB per month
minProvingPeriodInEpochs: 2880,
location: "US-East",
paymentTokenAddress: IERC20(address(0)) // Payment in FIL
paymentTokenAddress: IERC20(address(0)), // Payment in FIL
ipniPeerId: hex""
});

bytes memory encodedData = abi.encode(pdpData);
Expand Down Expand Up @@ -224,7 +227,8 @@ contract ServiceProviderRegistryTest is Test {
storagePricePerTibPerMonth: 500000000000000000,
minProvingPeriodInEpochs: 2880,
location: "US-East",
paymentTokenAddress: IERC20(address(0))
paymentTokenAddress: IERC20(address(0)),
ipniPeerId: hex""
});

bytes memory encodedData = abi.encode(pdpData);
Expand Down Expand Up @@ -259,7 +263,8 @@ contract ServiceProviderRegistryTest is Test {
storagePricePerTibPerMonth: 750000000000000000, // 0.75 FIL per TiB per month
minProvingPeriodInEpochs: 2880,
location: "US-East",
paymentTokenAddress: IERC20(address(0)) // Payment in FIL
paymentTokenAddress: IERC20(address(0)), // Payment in FIL
ipniPeerId: hex""
});

bytes memory encodedData = abi.encode(pdpData);
Expand Down Expand Up @@ -323,7 +328,8 @@ contract ServiceProviderRegistryTest is Test {
storagePricePerTibPerMonth: 500000000000000000, // 0.5 FIL per TiB per month
minProvingPeriodInEpochs: 2880,
location: "US-East",
paymentTokenAddress: IERC20(address(0)) // Payment in FIL
paymentTokenAddress: IERC20(address(0)), // Payment in FIL
ipniPeerId: hex""
});

// Encode PDP data
Expand Down Expand Up @@ -561,7 +567,8 @@ contract ServiceProviderRegistryTest is Test {
storagePricePerTibPerMonth: 1000,
minProvingPeriodInEpochs: 1,
location: "US",
paymentTokenAddress: IERC20(address(0))
paymentTokenAddress: IERC20(address(0)),
ipniPeerId: hex""
});
return abi.encode(pdpOffering);
}
Expand Down
185 changes: 183 additions & 2 deletions service_contracts/test/ServiceProviderRegistryFull.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ contract ServiceProviderRegistryFullTest is Test {
storagePricePerTibPerMonth: 1000000000000000000, // 1 FIL per TiB per month
minProvingPeriodInEpochs: 2880, // 1 day in epochs (30 second blocks)
location: "North America",
paymentTokenAddress: IERC20(address(0)) // Payment in FIL
paymentTokenAddress: IERC20(address(0)), // Payment in FIL
ipniPeerId: hex""
});

updatedPDPData = ServiceProviderRegistryStorage.PDPOffering({
Expand All @@ -73,7 +74,8 @@ contract ServiceProviderRegistryFullTest is Test {
storagePricePerTibPerMonth: 2000000000000000000, // 2 FIL per TiB per month
minProvingPeriodInEpochs: 1440, // 12 hours in epochs
location: "Europe",
paymentTokenAddress: IERC20(address(0)) // Payment in FIL
paymentTokenAddress: IERC20(address(0)), // Payment in FIL
ipniPeerId: hex""
});

// Encode PDP data
Expand Down Expand Up @@ -1800,4 +1802,183 @@ contract ServiceProviderRegistryFullTest is Test {
assertFalse(tierCleared, "Tier key should not exist after update");
assertEq(clearedTier, "", "Tier key should be cleared after update");
}

// ========== IPNI Peer ID Validation Tests ==========

function testRegisterWithEmptyIpniPeerId() public {
ServiceProviderRegistryStorage.PDPOffering memory validPDP = defaultPDPData;
validPDP.ipniPeerId = hex"";
bytes memory encodedPDP = abi.encode(validPDP);
string[] memory emptyKeys = new string[](0);
string[] memory emptyValues = new string[](0);

vm.prank(provider1);
uint256 providerId = registry.registerProvider{value: REGISTRATION_FEE}(
provider1,
"",
"Test provider",
ServiceProviderRegistryStorage.ProductType.PDP,
encodedPDP,
emptyKeys,
emptyValues
);
assertEq(providerId, 1, "Should succeed with empty ipniPeerId");

// Verify it was stored correctly
(ServiceProviderRegistryStorage.PDPOffering memory stored,,) = registry.getPDPService(providerId);
assertEq(stored.ipniPeerId.length, 0, "Empty IPNI peer ID should be stored");
}

function testRegisterWithMaxLengthIpniPeerId() public {
bytes memory maxPeerId = new bytes(128);
for (uint256 i = 0; i < 128; i++) {
maxPeerId[i] = 0xFF;
}

ServiceProviderRegistryStorage.PDPOffering memory validPDP = defaultPDPData;
validPDP.ipniPeerId = maxPeerId;
bytes memory encodedPDP = abi.encode(validPDP);
string[] memory emptyKeys = new string[](0);
string[] memory emptyValues = new string[](0);

vm.prank(provider1);
uint256 providerId = registry.registerProvider{value: REGISTRATION_FEE}(
provider1,
"",
"Test provider",
ServiceProviderRegistryStorage.ProductType.PDP,
encodedPDP,
emptyKeys,
emptyValues
);
assertEq(providerId, 1, "Should succeed with 128-byte ipniPeerId");

// Verify it was stored correctly
(ServiceProviderRegistryStorage.PDPOffering memory stored,,) = registry.getPDPService(providerId);
assertEq(stored.ipniPeerId.length, 128, "128-byte IPNI peer ID should be stored");
assertEq(stored.ipniPeerId, maxPeerId, "IPNI peer ID should match");
}

function testRegisterWithTooLongIpniPeerId() public {
bytes memory longPeerId = new bytes(129);
for (uint256 i = 0; i < 129; i++) {
longPeerId[i] = 0xFF;
}

ServiceProviderRegistryStorage.PDPOffering memory invalidPDP = defaultPDPData;
invalidPDP.ipniPeerId = longPeerId;
bytes memory encodedInvalidPDP = abi.encode(invalidPDP);
string[] memory emptyKeys = new string[](0);
string[] memory emptyValues = new string[](0);

vm.prank(provider1);
vm.expectRevert("IPNI peer ID too long");
registry.registerProvider{value: REGISTRATION_FEE}(
provider1,
"",
"Test provider",
ServiceProviderRegistryStorage.ProductType.PDP,
encodedInvalidPDP,
emptyKeys,
emptyValues
);
}

function testRegisterWithRealisticIpniPeerId() public {
// Typical libp2p peer ID is ~38 bytes (multihash of public key)
// Format: 0x1220<32-byte sha256 hash> for a total of 34 bytes
bytes memory realisticPeerId = hex"12205f8bb7e5e0e8e5f8e5e0e8e5f8e5e0e8e5f8e5e0e8e5f8e5e0e8e5f8abcdef01";

ServiceProviderRegistryStorage.PDPOffering memory validPDP = defaultPDPData;
validPDP.ipniPeerId = realisticPeerId;
bytes memory encodedPDP = abi.encode(validPDP);
string[] memory emptyKeys = new string[](0);
string[] memory emptyValues = new string[](0);

vm.prank(provider1);
uint256 providerId = registry.registerProvider{value: REGISTRATION_FEE}(
provider1,
"",
"Test provider",
ServiceProviderRegistryStorage.ProductType.PDP,
encodedPDP,
emptyKeys,
emptyValues
);

// Verify it was stored correctly
(ServiceProviderRegistryStorage.PDPOffering memory stored,,) = registry.getPDPService(providerId);
assertEq(stored.ipniPeerId, realisticPeerId, "IPNI peer ID should be stored correctly");
assertEq(stored.ipniPeerId.length, 34, "Realistic IPNI peer ID should be 34 bytes");
}

function testUpdateIpniPeerId() public {
// Register with empty IPNI peer ID
string[] memory emptyKeys = new string[](0);
string[] memory emptyValues = new string[](0);

vm.prank(provider1);
uint256 providerId = registry.registerProvider{value: REGISTRATION_FEE}(
provider1,
"",
"Test provider",
ServiceProviderRegistryStorage.ProductType.PDP,
encodedDefaultPDPData,
emptyKeys,
emptyValues
);

// Verify initial state
(ServiceProviderRegistryStorage.PDPOffering memory initialStored,,) = registry.getPDPService(providerId);
assertEq(initialStored.ipniPeerId.length, 0, "Initial IPNI peer ID should be empty");

// Update with a peer ID
bytes memory newPeerId = hex"12205f8bb7e5e0e8e5f8e5e0e8e5f8e5e0e8e5f8e5e0e8e5f8e5e0e8e5f8abcdef01";
ServiceProviderRegistryStorage.PDPOffering memory updatedData = updatedPDPData;
updatedData.ipniPeerId = newPeerId;
bytes memory encodedUpdatedData = abi.encode(updatedData);

vm.prank(provider1);
registry.updateProduct(
ServiceProviderRegistryStorage.ProductType.PDP, encodedUpdatedData, emptyKeys, emptyValues
);

// Verify update
(ServiceProviderRegistryStorage.PDPOffering memory updatedStored,,) = registry.getPDPService(providerId);
assertEq(updatedStored.ipniPeerId, newPeerId, "IPNI peer ID should be updated");
assertEq(updatedStored.ipniPeerId.length, 34, "Updated IPNI peer ID should be 34 bytes");
}

function testUpdateWithTooLongIpniPeerId() public {
// Register first
string[] memory emptyKeys = new string[](0);
string[] memory emptyValues = new string[](0);

vm.prank(provider1);
registry.registerProvider{value: REGISTRATION_FEE}(
provider1,
"",
"Test provider",
ServiceProviderRegistryStorage.ProductType.PDP,
encodedDefaultPDPData,
emptyKeys,
emptyValues
);

// Try to update with too long peer ID
bytes memory longPeerId = new bytes(129);
for (uint256 i = 0; i < 129; i++) {
longPeerId[i] = 0xAB;
}

ServiceProviderRegistryStorage.PDPOffering memory invalidData = updatedPDPData;
invalidData.ipniPeerId = longPeerId;
bytes memory encodedInvalidData = abi.encode(invalidData);

vm.prank(provider1);
vm.expectRevert("IPNI peer ID too long");
registry.updateProduct(
ServiceProviderRegistryStorage.ProductType.PDP, encodedInvalidData, emptyKeys, emptyValues
);
}
}
Loading
Loading