-
Notifications
You must be signed in to change notification settings - Fork 31
Expand file tree
/
Copy pathpdp-capabilities.ts
More file actions
143 lines (133 loc) · 5.84 KB
/
Copy pathpdp-capabilities.ts
File metadata and controls
143 lines (133 loc) · 5.84 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
import { base58btc } from 'multiformats/bases/base58'
import type { Hex } from 'viem'
import { bytesToHex, fromHex, hexToString, isHex, numberToBytes, stringToHex, toBytes } from 'viem'
import { z } from 'zod'
import { ZodValidationError } from '../errors/base.ts'
import type { PDPOffering, ProviderWithProduct } from '../sp-registry/types.ts'
import { capabilitiesListToObject, decodeAddressCapability } from './capabilities.ts'
import { zHex } from './schemas.ts'
/**
* Zod schema for PDP offering
*
* @see https://github.com/FilOzone/filecoin-services/blob/a86e4a5018133f17a25b4bb6b5b99da4d34fe664/service_contracts/src/ServiceProviderRegistry.sol#L14
*/
export const PDPOfferingSchema = z
.object({
serviceURL: zHex,
minPieceSizeInBytes: zHex,
maxPieceSizeInBytes: zHex,
storagePricePerTibPerDay: zHex,
minProvingPeriodInEpochs: zHex,
location: zHex,
paymentTokenAddress: zHex,
ipniPiece: zHex.optional(),
ipniIpfs: zHex.optional(),
ipniPeerId: zHex.optional(),
})
.catchall(zHex)
// Standard capability keys for PDP product type (must match ServiceProviderRegistry.sol REQUIRED_PDP_KEYS)
export const CAP_SERVICE_URL = 'serviceURL'
export const CAP_MIN_PIECE_SIZE = 'minPieceSizeInBytes'
export const CAP_MAX_PIECE_SIZE = 'maxPieceSizeInBytes'
export const CAP_STORAGE_PRICE = 'storagePricePerTibPerDay'
export const CAP_MIN_PROVING_PERIOD = 'minProvingPeriodInEpochs'
export const CAP_LOCATION = 'location'
export const CAP_PAYMENT_TOKEN = 'paymentTokenAddress'
export const CAP_IPNI_PIECE = 'ipniPiece' // Optional (not validated by Bloom filter)
export const CAP_IPNI_IPFS = 'ipniIpfs' // Optional (not validated by Bloom filter)
export const CAP_IPNI_PEER_ID = 'ipniPeerId' // Optional (not validated by Bloom filter)
/** @deprecated Use CAP_IPNI_PEER_ID - kept for reading legacy entries */
export const CAP_IPNI_PEER_ID_LEGACY = 'IPNIPeerID'
export function decodePDPOffering(provider: ProviderWithProduct): PDPOffering {
const capabilities = capabilitiesListToObject(provider.product.capabilityKeys, provider.productCapabilityValues)
const parsed = PDPOfferingSchema.safeParse(capabilities)
if (!parsed.success) {
throw new ZodValidationError(parsed.error)
}
return decodePDPCapabilities(parsed.data)
}
/** Capability keys that are decoded into typed PDPOffering fields, derived from the schema */
const KNOWN_CAPABILITY_KEYS = new Set([...Object.keys(PDPOfferingSchema.shape), CAP_IPNI_PEER_ID_LEGACY])
/**
* Decode PDP capabilities from keys/values arrays into a PDPOffering object.
* Based on Curio's capabilitiesToOffering function.
*/
export function decodePDPCapabilities(capabilities: Record<string, Hex>): PDPOffering {
const required = {
serviceURL: hexToString(capabilities.serviceURL),
minPieceSizeInBytes: BigInt(capabilities.minPieceSizeInBytes),
maxPieceSizeInBytes: BigInt(capabilities.maxPieceSizeInBytes),
storagePricePerTibPerDay: BigInt(capabilities.storagePricePerTibPerDay),
minProvingPeriodInEpochs: BigInt(capabilities.minProvingPeriodInEpochs),
location: hexToString(capabilities.location),
paymentTokenAddress: decodeAddressCapability(capabilities.paymentTokenAddress),
}
const optional = {
ipniPiece: CAP_IPNI_PIECE in capabilities ? capabilities[CAP_IPNI_PIECE] === '0x01' : false,
ipniIpfs: CAP_IPNI_IPFS in capabilities ? capabilities[CAP_IPNI_IPFS] === '0x01' : false,
ipniPeerId:
CAP_IPNI_PEER_ID in capabilities
? base58btc.encode(fromHex(capabilities[CAP_IPNI_PEER_ID], 'bytes'))
: CAP_IPNI_PEER_ID_LEGACY in capabilities
? base58btc.encode(fromHex(capabilities[CAP_IPNI_PEER_ID_LEGACY], 'bytes'))
: undefined,
}
const extraCapabilities: Record<string, Hex> = Object.create(null)
for (const key of Object.keys(capabilities)) {
if (!KNOWN_CAPABILITY_KEYS.has(key)) {
extraCapabilities[key] = capabilities[key]
}
}
return { ...required, ...optional, extraCapabilities }
}
/**
* Encode PDP capabilities from a PDPOffering object and a capabilities object into a capability keys and values array.
*
* @todo add zod schema validation for the pdp offering and capabilities.
*
* @param pdpOffering - The PDP offering to encode.
* @param capabilities - The capabilities to encode.
* @returns The encoded capability keys and values.
*/
export function encodePDPCapabilities(
pdpOffering: PDPOffering,
capabilities?: Record<string, string>
): [string[], Hex[]] {
const capabilityKeys = []
const capabilityValues: Hex[] = []
capabilityKeys.push(CAP_SERVICE_URL)
capabilityValues.push(stringToHex(pdpOffering.serviceURL))
capabilityKeys.push(CAP_MIN_PIECE_SIZE)
capabilityValues.push(bytesToHex(numberToBytes(pdpOffering.minPieceSizeInBytes)))
capabilityKeys.push(CAP_MAX_PIECE_SIZE)
capabilityValues.push(bytesToHex(numberToBytes(pdpOffering.maxPieceSizeInBytes)))
if (pdpOffering.ipniPiece) {
capabilityKeys.push(CAP_IPNI_PIECE)
capabilityValues.push('0x01')
}
if (pdpOffering.ipniIpfs) {
capabilityKeys.push(CAP_IPNI_IPFS)
capabilityValues.push('0x01')
}
capabilityKeys.push(CAP_STORAGE_PRICE)
capabilityValues.push(bytesToHex(numberToBytes(pdpOffering.storagePricePerTibPerDay)))
capabilityKeys.push(CAP_MIN_PROVING_PERIOD)
capabilityValues.push(bytesToHex(numberToBytes(pdpOffering.minProvingPeriodInEpochs)))
capabilityKeys.push(CAP_LOCATION)
capabilityValues.push(stringToHex(pdpOffering.location))
capabilityKeys.push(CAP_PAYMENT_TOKEN)
capabilityValues.push(pdpOffering.paymentTokenAddress)
if (capabilities != null) {
for (const [key, value] of Object.entries(capabilities)) {
capabilityKeys.push(key)
if (!value) {
capabilityValues.push('0x01')
} else if (isHex(value)) {
capabilityValues.push(value)
} else {
capabilityValues.push(bytesToHex(toBytes(value)))
}
}
}
return [capabilityKeys, capabilityValues]
}