forked from FilOzone/synapse-sdk
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcreate-dataset-add-pieces.ts
More file actions
240 lines (229 loc) · 8.8 KB
/
Copy pathcreate-dataset-add-pieces.ts
File metadata and controls
240 lines (229 loc) · 8.8 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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
import { HttpError, type RequestErrors, type RequestJsonErrors, request } from 'iso-web/http'
import type { ToString } from 'multiformats'
import { type Account, type Address, type Chain, type Client, type Hex, isHex, type Transport } from 'viem'
import { asChain } from '../chains.ts'
import { CreateDataSetError, LocationHeaderError } from '../errors/index.ts'
import type {
WaitForAddPiecesError,
WaitForAddPiecesRejectedError,
WaitForCreateDataSetError,
WaitForCreateDataSetRejectedError,
} from '../errors/pdp.ts'
import type { PieceCID } from '../piece/piece-cid.ts'
import { signCreateDataSetAndAddPieces } from '../typed-data/sign-create-dataset-add-pieces.ts'
import { RETRY_CONSTANTS } from '../utils/constants.ts'
import { datasetMetadataObjectToEntry, type MetadataObject, pieceMetadataObjectToEntry } from '../utils/metadata.ts'
import { validateAddPiecesBatch, waitForAddPieces } from './add-pieces.ts'
import { waitForCreateDataSet } from './create-dataset.ts'
export namespace createDataSetAndAddPiecesApiRequest {
export type OptionsType = {
/** The service URL of the PDP API. */
serviceURL: string
/** The address of the record keeper. */
recordKeeper: Address
/** The extra data for the create data set and add pieces. */
extraData: Hex
/** The pieces to add. */
pieces: PieceCID[]
/** The number of retries. Defaults to 2. */
retryCount?: number
/** The delay with exponential backoff between retries in milliseconds. Defaults to {@link RETRY_CONSTANTS.RETRY_DELAY}. */
retryDelay?: number
}
export type OutputType = {
/** The transaction hash. */
txHash: Hex
/** The status URL. */
statusUrl: string
}
export type ErrorType = CreateDataSetError | LocationHeaderError | RequestErrors
export type RequestBody = {
recordKeeper: Address
extraData: Hex
pieces: {
pieceCid: ToString<PieceCID>
subPieces: { subPieceCid: ToString<PieceCID> }[]
}[]
}
}
/**
* Create a data set and add pieces to it on PDP API
*
* POST /pdp/data-sets/create-and-add
*
* @param options - {@link createDataSetAndAddPiecesApiRequest.OptionsType}
* @returns Hash and status URL {@link createDataSetAndAddPiecesApiRequest.OutputType}
* @throws Errors {@link createDataSetAndAddPiecesApiRequest.ErrorType}
*/
export async function createDataSetAndAddPiecesApiRequest(
options: createDataSetAndAddPiecesApiRequest.OptionsType
): Promise<createDataSetAndAddPiecesApiRequest.OutputType> {
// Send the create data set message to the PDP
const response = await request.post(new URL(`pdp/data-sets/create-and-add`, options.serviceURL), {
json: {
recordKeeper: options.recordKeeper,
extraData: options.extraData,
pieces: options.pieces.map((piece) => ({
pieceCid: piece.toString(),
subPieces: [{ subPieceCid: piece.toString() }],
})),
},
timeout: RETRY_CONSTANTS.TIMEOUT,
retry: {
retries: options.retryCount,
minTimeout: options.retryDelay ?? RETRY_CONSTANTS.RETRY_DELAY,
shouldRetry: (ctx) => HttpError.is(ctx.error) && ctx.error.code === 429,
},
})
if (response.error) {
if (HttpError.is(response.error)) {
throw new CreateDataSetError(await response.error.response.text())
}
throw response.error
}
const location = response.result.headers.get('Location')
const hash = location?.split('/').pop()
if (!location || !hash || !isHex(hash)) {
throw new LocationHeaderError(location)
}
return {
txHash: hash,
statusUrl: new URL(location, options.serviceURL).toString(),
}
}
export type CreateDataSetAndAddPiecesOptions = {
/** The service URL of the PDP API. */
serviceURL: string
/** The address that will receive payments (service provider). */
payee: Address
/** The pieces and metadata to add to the data set. */
pieces: { pieceCid: PieceCID; metadata?: MetadataObject }[]
/**
* The address that will pay for the storage (client). If not provided, the default is the client address.
*
* If client is from a session key this should be set to the actual payer address
*/
payer?: Address
/** The metadata for the data set. */
metadata?: MetadataObject
/** The client data set id (nonce) to use for the signature. Must be unique for each data set. */
clientDataSetId?: bigint
/** Pre-built signed extraData. When provided, skips internal EIP-712 signing. */
extraData?: Hex
/** Whether the data set should use CDN. */
cdn?: boolean
/** The address of the record keeper to use for the signature. If not provided, the default is the Warm Storage contract address. */
recordKeeper?: Address
/** The number of retries. Defaults to 2. */
retryCount?: number
/** The delay with exponential backoff between retries in milliseconds. Defaults to {@link RETRY_CONSTANTS.RETRY_DELAY}. */
retryDelay?: number
}
export namespace createDataSetAndAddPieces {
export type OptionsType = CreateDataSetAndAddPiecesOptions
export type ReturnType = createDataSetAndAddPiecesApiRequest.OutputType
export type ErrorType = createDataSetAndAddPiecesApiRequest.ErrorType | signCreateDataSetAndAddPieces.ErrorType
}
/**
* Create a data set and add pieces to it
*
* @param client - The client to use to create the data set.
* @param options - {@link CreateDataSetAndAddPiecesOptions}
* @returns The response from the create data set on PDP API. {@link createDataSetAndAddPieces.ReturnType}
* @throws Errors {@link createDataSetAndAddPieces.ErrorType}
*/
export async function createDataSetAndAddPieces(
client: Client<Transport, Chain, Account>,
options: CreateDataSetAndAddPiecesOptions
): Promise<createDataSetAndAddPieces.ReturnType> {
validateAddPiecesBatch(options.pieces.length)
const chain = asChain(client.chain)
const extraData =
options.extraData ??
(await signCreateDataSetAndAddPieces(client, {
clientDataSetId: options.clientDataSetId,
payee: options.payee,
payer: options.payer,
metadata: datasetMetadataObjectToEntry(options.metadata, {
cdn: options.cdn ?? false,
}),
pieces: options.pieces.map((piece) => ({
pieceCid: piece.pieceCid,
metadata: pieceMetadataObjectToEntry(piece.metadata),
})),
}))
return createDataSetAndAddPiecesApiRequest({
serviceURL: options.serviceURL,
recordKeeper: options.recordKeeper ?? chain.contracts.fwss.address,
extraData,
pieces: options.pieces.map((piece) => piece.pieceCid),
retryCount: options.retryCount,
retryDelay: options.retryDelay,
})
}
export namespace waitForCreateDataSetAddPieces {
export type OptionsType = {
/** The status URL to poll. */
statusUrl: string
/** The timeout in milliseconds. Defaults to 5 minutes. */
timeout?: number
/** The number of retries. Defaults to 2. */
retryCount?: number
/** The delay with exponential backoff between retries in milliseconds. Defaults to {@link RETRY_CONSTANTS.RETRY_DELAY}. */
retryDelay?: number
/** The poll interval in milliseconds. Defaults to {@link RETRY_CONSTANTS.POLL_INTERVAL}. */
pollInterval?: number
}
export type ReturnType = {
/** Original Location / wait-key hash (not necessarily the included on-chain hash). */
hash: string
/**
* Hash included on chain once confirmed. Differs from hash after replace-by-fee.
* For explorers and receipt lookups use `confirmedTxHash ?? hash`.
*/
confirmedTxHash?: string
dataSetId: bigint
piecesIds: bigint[]
}
export type ErrorType =
| WaitForCreateDataSetError
| WaitForCreateDataSetRejectedError
| WaitForAddPiecesError
| WaitForAddPiecesRejectedError
| RequestJsonErrors
}
/**
* Wait for the data set creation status.
*
* GET /pdp/data-sets/created({txHash})
*
* @param options - {@link waitForCreateDataSetAddPieces.OptionsType}
* @returns Status {@link waitForCreateDataSetAddPieces.ReturnType}
* @throws Errors {@link waitForCreateDataSetAddPieces.ErrorType}
*/
export async function waitForCreateDataSetAddPieces(
options: waitForCreateDataSetAddPieces.OptionsType
): Promise<waitForCreateDataSetAddPieces.ReturnType> {
const origin = new URL(options.statusUrl).origin
const createdDataset = await waitForCreateDataSet({
statusUrl: options.statusUrl,
retryCount: options.retryCount,
retryDelay: options.retryDelay,
pollInterval: options.pollInterval,
})
const addedPieces = await waitForAddPieces({
statusUrl: new URL(
`/pdp/data-sets/${createdDataset.dataSetId}/pieces/added/${createdDataset.createMessageHash}`,
origin
).toString(),
retryCount: options.retryCount,
retryDelay: options.retryDelay,
pollInterval: options.pollInterval,
})
return {
hash: createdDataset.createMessageHash,
confirmedTxHash: createdDataset.confirmedTxHash ?? addedPieces.confirmedTxHash,
dataSetId: createdDataset.dataSetId,
piecesIds: addedPieces.confirmedPieceIds,
}
}