Skip to content

Commit eca0ad8

Browse files
committed
feat(core): preserve non-standard SP registry capabilities in PDPOffering
There's potentially useful signals in here downstream from our consumption of the capabilities providers publish.
1 parent 8b85290 commit eca0ad8

4 files changed

Lines changed: 70 additions & 13 deletions

File tree

packages/synapse-core/src/sp-registry/types.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,4 +71,10 @@ export interface PDPOffering {
7171
* The IPNI peer ID.
7272
*/
7373
ipniPeerId?: string
74+
/**
75+
* Additional non-standard capabilities declared by the provider.
76+
* Keys are capability names as registered on-chain (plain strings),
77+
* values are the raw hex-encoded bytes from the contract.
78+
*/
79+
extraCapabilities?: Record<string, Hex>
7480
}

packages/synapse-core/src/utils/pdp-capabilities.ts

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,20 @@ import { zHex } from './schemas.ts'
1212
*
1313
* @see https://github.com/FilOzone/filecoin-services/blob/a86e4a5018133f17a25b4bb6b5b99da4d34fe664/service_contracts/src/ServiceProviderRegistry.sol#L14
1414
*/
15-
export const PDPOfferingSchema = z.object({
16-
serviceURL: zHex,
17-
minPieceSizeInBytes: zHex,
18-
maxPieceSizeInBytes: zHex,
19-
storagePricePerTibPerDay: zHex,
20-
minProvingPeriodInEpochs: zHex,
21-
location: zHex,
22-
paymentTokenAddress: zHex,
23-
ipniPiece: zHex.optional(),
24-
ipniIpfs: zHex.optional(),
25-
ipniPeerId: zHex.optional(),
26-
})
15+
export const PDPOfferingSchema = z
16+
.object({
17+
serviceURL: zHex,
18+
minPieceSizeInBytes: zHex,
19+
maxPieceSizeInBytes: zHex,
20+
storagePricePerTibPerDay: zHex,
21+
minProvingPeriodInEpochs: zHex,
22+
location: zHex,
23+
paymentTokenAddress: zHex,
24+
ipniPiece: zHex.optional(),
25+
ipniIpfs: zHex.optional(),
26+
ipniPeerId: zHex.optional(),
27+
})
28+
.catchall(zHex)
2729
// Standard capability keys for PDP product type (must match ServiceProviderRegistry.sol REQUIRED_PDP_KEYS)
2830
export const CAP_SERVICE_URL = 'serviceURL'
2931
export const CAP_MIN_PIECE_SIZE = 'minPieceSizeInBytes'
@@ -47,6 +49,9 @@ export function decodePDPOffering(provider: ProviderWithProduct): PDPOffering {
4749
return decodePDPCapabilities(parsed.data)
4850
}
4951

52+
/** Capability keys that are decoded into typed PDPOffering fields, derived from the schema */
53+
const KNOWN_CAPABILITY_KEYS = new Set([...Object.keys(PDPOfferingSchema.shape), CAP_IPNI_PEER_ID_LEGACY])
54+
5055
/**
5156
* Decode PDP capabilities from keys/values arrays into a PDPOffering object.
5257
* Based on Curio's capabilitiesToOffering function.
@@ -71,7 +76,15 @@ export function decodePDPCapabilities(capabilities: Record<string, Hex>): PDPOff
7176
? base58btc.encode(fromHex(capabilities[CAP_IPNI_PEER_ID_LEGACY], 'bytes'))
7277
: undefined,
7378
}
74-
return { ...required, ...optional }
79+
80+
const extraCapabilities: Record<string, Hex> = Object.create(null)
81+
for (const key of Object.keys(capabilities)) {
82+
if (!KNOWN_CAPABILITY_KEYS.has(key)) {
83+
extraCapabilities[key] = capabilities[key]
84+
}
85+
}
86+
87+
return { ...required, ...optional, extraCapabilities }
7588
}
7689

7790
/**

packages/synapse-core/test/pdp-capabilities.test.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,43 @@ describe('decodePDPCapabilities', () => {
2727
assert.strictEqual(result.ipniPeerId, undefined)
2828
})
2929
})
30+
31+
describe('extraCapabilities', () => {
32+
it('preserves non-standard capabilities in extraCapabilities', () => {
33+
const capabilities = createMinimalCapabilities({
34+
serviceStatus: toHex('dev'),
35+
customFlag: '0x01',
36+
})
37+
38+
const result = decodePDPCapabilities(capabilities)
39+
40+
assert.ok(result.extraCapabilities)
41+
assert.strictEqual(result.extraCapabilities.serviceStatus, toHex('dev'))
42+
assert.strictEqual(result.extraCapabilities.customFlag, '0x01')
43+
})
44+
45+
it('returns empty extraCapabilities when no non-standard capabilities exist', () => {
46+
const capabilities = createMinimalCapabilities()
47+
48+
const result = decodePDPCapabilities(capabilities)
49+
50+
assert.ok(result.extraCapabilities)
51+
assert.strictEqual(Object.keys(result.extraCapabilities).length, 0)
52+
})
53+
54+
it('does not include standard capabilities in extraCapabilities', () => {
55+
const capabilities = createMinimalCapabilities({
56+
serviceStatus: toHex('dev'),
57+
})
58+
59+
const result = decodePDPCapabilities(capabilities)
60+
61+
assert.ok(result.extraCapabilities)
62+
assert.strictEqual(Object.keys(result.extraCapabilities).length, 1)
63+
assert.strictEqual(result.extraCapabilities.serviceURL, undefined)
64+
assert.strictEqual(result.extraCapabilities.location, undefined)
65+
})
66+
})
3067
})
3168

3269
// Minimal valid capabilities for testing (all required fields)

packages/synapse-sdk/src/test/storage.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1270,6 +1270,7 @@ describe('StorageService', () => {
12701270
minProvingPeriodInEpochs: 30n,
12711271
location: 'us-east',
12721272
paymentTokenAddress: '0xb3042734b608a1b16e9e86b374a3f3e389b4cdf0',
1273+
extraCapabilities: {},
12731274
},
12741275
})
12751276
})

0 commit comments

Comments
 (0)