@@ -10,7 +10,12 @@ import {Cids} from "@pdp/Cids.sol";
1010import {MyERC1967Proxy} from "@pdp/ERC1967Proxy.sol " ;
1111import {SessionKeyRegistry} from "@session-key-registry/SessionKeyRegistry.sol " ;
1212
13- import {CHALLENGES_PER_PROOF, FilecoinWarmStorageService} from "../src/FilecoinWarmStorageService.sol " ;
13+ import {
14+ CHALLENGES_PER_PROOF,
15+ MAX_ADD_PIECES_EXTRA_DATA_SIZE,
16+ MAX_CREATE_DATA_SET_EXTRA_DATA_SIZE,
17+ FilecoinWarmStorageService
18+ } from "../src/FilecoinWarmStorageService.sol " ;
1419import {FilecoinWarmStorageServiceStateView} from "../src/FilecoinWarmStorageServiceStateView.sol " ;
1520import {SignatureVerificationLib} from "../src/lib/SignatureVerificationLib.sol " ;
1621import {FilecoinWarmStorageServiceStateLibrary} from "../src/lib/FilecoinWarmStorageServiceStateLibrary.sol " ;
@@ -243,6 +248,22 @@ contract FilecoinWarmStorageServiceTest is MockFVMTest {
243248 );
244249 }
245250
251+ function _generateKey (uint256 index ) public pure returns (string memory ) {
252+ bytes32 hash = keccak256 (abi.encodePacked ("base_salt " , index));
253+
254+ // Convert hash to hex string
255+ string memory hexStr = Strings.toHexString (uint256 (hash), 32 ); // 0x + 64 chars
256+
257+ // Remove the "0x" prefix and take only first 32 characters to make exactly 32 bytes
258+ bytes memory keyBytes = bytes (hexStr);
259+ bytes memory result = new bytes (32 );
260+ for (uint256 i = 0 ; i < 32 ; i++ ) {
261+ result[i] = keyBytes[i + 2 ]; // skip '0x'
262+ }
263+
264+ return string (result); // exactly 32-byte unique string
265+ }
266+
246267 function testInitialState () public view {
247268 assertEq (
248269 pdpServiceWithPayments.pdpVerifierAddress (),
@@ -2517,6 +2538,55 @@ contract FilecoinWarmStorageServiceTest is MockFVMTest {
25172538 }
25182539 }
25192540
2541+ function testDataSetMetaDataWithAllBoundaries () public {
2542+ // Create metadata with max keys, each with max key and value lengths
2543+ uint256 [] memory keyCounts = new uint256 [](3 );
2544+ keyCounts[0 ] = MAX_KEYS_PER_DATASET - 1 ; // Just below max
2545+ keyCounts[1 ] = MAX_KEYS_PER_DATASET; // At max
2546+ keyCounts[2 ] = MAX_KEYS_PER_DATASET + 4 ; // Exceeds max
2547+
2548+ for (uint256 testIdx = 0 ; testIdx < keyCounts.length ; testIdx++ ) {
2549+ uint256 keyCount = keyCounts[testIdx];
2550+ string [] memory metadataKeys = new string [](keyCount);
2551+ string [] memory metadataValues = new string [](keyCount);
2552+
2553+ for (uint256 i = 0 ; i < keyCount; i++ ) {
2554+ //key must be unique with length 32 bytes
2555+ metadataKeys[i] = _generateKey (i);
2556+ assertEq (bytes (metadataKeys[i]).length , 32 , "Key length should be 32 bytes " );
2557+ // Max key length
2558+ metadataValues[i] = _makeStringOfLength (128 ); // Max value length
2559+ }
2560+
2561+ if (keyCount <= MAX_KEYS_PER_DATASET) {
2562+ // Should succeed for valid counts
2563+ uint256 dataSetId = createDataSetForClient (sp1, client, metadataKeys, metadataValues);
2564+
2565+ // Verify all metadata keys and values
2566+ for (uint256 i = 0 ; i < metadataKeys.length ; i++ ) {
2567+ (bool exists , string memory storedMetadata ) =
2568+ viewContract.getDataSetMetadata (dataSetId, metadataKeys[i]);
2569+ assertTrue (exists, string .concat ("Key " , metadataKeys[i], " should exist " ));
2570+ assertEq (
2571+ storedMetadata,
2572+ metadataValues[i],
2573+ string .concat ("Stored metadata for " , metadataKeys[i], " should match " )
2574+ );
2575+ }
2576+ } else {
2577+ // Should fail for exceeding max
2578+ bytes memory encodedData = prepareDataSetForClient (sp1, client, metadataKeys, metadataValues);
2579+ vm.prank (sp1);
2580+ vm.expectRevert (
2581+ abi.encodeWithSelector (
2582+ Errors.ExtraDataTooLarge.selector , encodedData.length , MAX_CREATE_DATA_SET_EXTRA_DATA_SIZE
2583+ )
2584+ );
2585+ mockPDPVerifier.createDataSet (pdpServiceWithPayments, encodedData);
2586+ }
2587+ }
2588+ }
2589+
25202590 function setupDataSetWithPieceMetadata (
25212591 uint256 pieceId ,
25222592 string [] memory keys ,
@@ -2778,6 +2848,91 @@ contract FilecoinWarmStorageServiceTest is MockFVMTest {
27782848 }
27792849 }
27802850
2851+ function testPieceMetadataAllBoundaries () public {
2852+ uint256 pieceId = 42 ;
2853+
2854+ // Helper to create a dataset
2855+ (string [] memory metadataKeys , string [] memory metadataValues ) =
2856+ _getSingleMetadataKV ("label " , "Test Root Metadata " );
2857+ uint256 dataSetId = createDataSetForClient (sp1, client, metadataKeys, metadataValues);
2858+
2859+ // Parameters
2860+ uint256 totalPieces = 5 ;
2861+
2862+ // --- Phase 1: all pieces within limits ---
2863+ {
2864+ Cids.Cid[] memory pieceData = new Cids.Cid [](totalPieces);
2865+ string [][] memory allKeys = new string [][](totalPieces);
2866+ string [][] memory allValues = new string [][](totalPieces);
2867+
2868+ for (uint256 p = 0 ; p < totalPieces; p++ ) {
2869+ pieceData[p] = Cids.CommPv2FromDigest (0 , 4 , keccak256 (abi.encodePacked ("file " , Strings.toString (p))));
2870+
2871+ uint256 keyCount = MAX_KEYS_PER_PIECE; // at the limit
2872+ string [] memory keys = new string [](keyCount);
2873+ string [] memory values = new string [](keyCount);
2874+
2875+ for (uint256 k = 0 ; k < keyCount; k++ ) {
2876+ // Generate globally unique keys by combining piece index and key index
2877+ keys[k] = _generateKey (p * 1000 + k);
2878+ assertEq (bytes (keys[k]).length , 32 , "Key length should be 32 bytes " );
2879+ values[k] = _makeStringOfLength (128 ); // max value length
2880+ }
2881+
2882+ allKeys[p] = keys;
2883+ allValues[p] = values;
2884+ }
2885+
2886+ uint256 nonce = pieceId + 1000 ;
2887+ bytes memory encodedData = abi.encode (nonce, allKeys, allValues, FAKE_SIGNATURE);
2888+ // Expect success
2889+ vm.expectEmit (true , false , false , true );
2890+ emit FilecoinWarmStorageService.PieceAdded (dataSetId, pieceId, pieceData[0 ], allKeys[0 ], allValues[0 ]);
2891+
2892+ vm.prank (address (mockPDPVerifier));
2893+ pdpServiceWithPayments.piecesAdded (dataSetId, pieceId, pieceData, encodedData);
2894+ console.log ("encodedData length (within limits): " , encodedData.length );
2895+ }
2896+
2897+ // --- Phase 2: one piece exceeds key limit and must revert ---
2898+ {
2899+ Cids.Cid[] memory pieceData = new Cids.Cid [](totalPieces);
2900+ string [][] memory allKeys = new string [][](totalPieces);
2901+ string [][] memory allValues = new string [][](totalPieces);
2902+
2903+ for (uint256 p = 0 ; p < totalPieces; p++ ) {
2904+ pieceData[p] = Cids.CommPv2FromDigest (0 , 4 , keccak256 (abi.encodePacked ("file-ex " , Strings.toString (p))));
2905+
2906+ // Make the last piece exceed the per-piece key limit
2907+ uint256 keyCount = (p == totalPieces - 1 ) ? (MAX_KEYS_PER_PIECE + 1 ) : MAX_KEYS_PER_PIECE;
2908+ string [] memory keys = new string [](keyCount);
2909+ string [] memory values = new string [](keyCount);
2910+
2911+ for (uint256 k = 0 ; k < keyCount; k++ ) {
2912+ // Ensure uniqueness across all pieces
2913+ keys[k] = _generateKey (p * 1000 + k + 1 );
2914+ values[k] = _makeStringOfLength (128 );
2915+ }
2916+
2917+ allKeys[p] = keys;
2918+ allValues[p] = values;
2919+ }
2920+
2921+ uint256 nonce = pieceId + 2000 ;
2922+ bytes memory encodedData = abi.encode (nonce, allKeys, allValues, FAKE_SIGNATURE);
2923+
2924+ // Expect revert when at least one piece has too many keys or extraData becomes too large
2925+ vm.prank (address (mockPDPVerifier));
2926+ vm.expectRevert (
2927+ abi.encodeWithSelector (
2928+ Errors.ExtraDataTooLarge.selector , encodedData.length , MAX_ADD_PIECES_EXTRA_DATA_SIZE
2929+ )
2930+ );
2931+ pdpServiceWithPayments.piecesAdded (dataSetId, pieceId + totalPieces, pieceData, encodedData);
2932+ console.log ("encodedData length (exceeding limits): " , encodedData.length );
2933+ }
2934+ }
2935+
27812936 function testPieceMetadataForSameKeyCannotRewrite () public {
27822937 uint256 pieceId = 42 ;
27832938
0 commit comments