@cardanowall/sdk-ts 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/index.cjs +1146 -365
- package/dist/client/index.cjs.map +1 -1
- package/dist/client/index.d.cts +48 -7
- package/dist/client/index.d.ts +48 -7
- package/dist/client/index.js +1144 -367
- package/dist/client/index.js.map +1 -1
- package/dist/conformance/cli.cjs +4400 -2121
- package/dist/conformance/cli.cjs.map +1 -1
- package/dist/conformance/cli.js +4401 -2122
- package/dist/conformance/cli.js.map +1 -1
- package/dist/fetch/index.cjs +33 -14
- package/dist/fetch/index.cjs.map +1 -1
- package/dist/fetch/index.d.cts +2 -2
- package/dist/fetch/index.d.ts +2 -2
- package/dist/fetch/index.js +32 -15
- package/dist/fetch/index.js.map +1 -1
- package/dist/{fetch-outbound-BT5-NiYN.d.cts → fetch-outbound-dOK3ZxYa.d.cts} +7 -3
- package/dist/{fetch-outbound-BT5-NiYN.d.ts → fetch-outbound-dOK3ZxYa.d.ts} +7 -3
- package/dist/hash/index.cjs +1 -1
- package/dist/hash/index.cjs.map +1 -1
- package/dist/hash/index.js +1 -1
- package/dist/hash/index.js.map +1 -1
- package/dist/identity/index.cjs +356 -230
- package/dist/identity/index.cjs.map +1 -1
- package/dist/identity/index.d.cts +3 -2
- package/dist/identity/index.d.ts +3 -2
- package/dist/identity/index.js +356 -230
- package/dist/identity/index.js.map +1 -1
- package/dist/index.cjs +5480 -2520
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -7
- package/dist/index.d.ts +7 -7
- package/dist/index.js +5460 -2516
- package/dist/index.js.map +1 -1
- package/dist/merkle/index.cjs +1 -1
- package/dist/merkle/index.js +1 -1
- package/dist/types-Cexm4VH9.d.cts +119 -0
- package/dist/types-CgoBub9J.d.ts +119 -0
- package/dist/{types-DGsZTMuZ.d.cts → types-DNu_IrWZ.d.cts} +236 -7
- package/dist/{types-DGsZTMuZ.d.ts → types-DNu_IrWZ.d.ts} +236 -7
- package/dist/verifier/index.cjs +4419 -2147
- package/dist/verifier/index.cjs.map +1 -1
- package/dist/verifier/index.d.cts +159 -111
- package/dist/verifier/index.d.ts +159 -111
- package/dist/verifier/index.js +4407 -2143
- package/dist/verifier/index.js.map +1 -1
- package/package.json +3 -3
- package/dist/types-B8Q3gW54.d.ts +0 -123
- package/dist/types-CLXdbjqr.d.cts +0 -123
package/dist/client/index.cjs
CHANGED
|
@@ -5,21 +5,21 @@ var sorts = require('cbor2/sorts');
|
|
|
5
5
|
var blake2_js = require('@noble/hashes/blake2.js');
|
|
6
6
|
var ed = require('@noble/ed25519');
|
|
7
7
|
var sha2_js = require('@noble/hashes/sha2.js');
|
|
8
|
-
require('hash-wasm');
|
|
8
|
+
var hashWasm = require('hash-wasm');
|
|
9
9
|
var zod = require('zod');
|
|
10
10
|
var utils_js = require('@noble/ciphers/utils.js');
|
|
11
|
-
var hmac_js = require('@noble/hashes/hmac.js');
|
|
12
11
|
var chacha_js = require('@noble/ciphers/chacha.js');
|
|
13
|
-
var hkdf_js = require('@noble/hashes/hkdf.js');
|
|
14
12
|
require('@noble/curves/abstract/edwards.js');
|
|
15
13
|
require('@noble/curves/abstract/montgomery.js');
|
|
16
14
|
require('@noble/curves/abstract/weierstrass.js');
|
|
17
15
|
var ed25519_js = require('@noble/curves/ed25519.js');
|
|
18
16
|
require('@noble/curves/nist.js');
|
|
19
17
|
var utils_js$2 = require('@noble/curves/utils.js');
|
|
18
|
+
var hkdf_js = require('@noble/hashes/hkdf.js');
|
|
20
19
|
var sha3_js = require('@noble/hashes/sha3.js');
|
|
21
20
|
var utils_js$1 = require('@noble/hashes/utils.js');
|
|
22
21
|
var fft_js = require('@noble/curves/abstract/fft.js');
|
|
22
|
+
var hmac_js = require('@noble/hashes/hmac.js');
|
|
23
23
|
|
|
24
24
|
function _interopNamespace(e) {
|
|
25
25
|
if (e && e.__esModule) return e;
|
|
@@ -103,6 +103,14 @@ function encodeCoseSign1(args) {
|
|
|
103
103
|
function sha256(input) {
|
|
104
104
|
return sha2_js.sha256(input);
|
|
105
105
|
}
|
|
106
|
+
async function sha256Stream(source) {
|
|
107
|
+
const hasher = await hashWasm.createSHA256();
|
|
108
|
+
hasher.init();
|
|
109
|
+
for await (const chunk of source) {
|
|
110
|
+
hasher.update(chunk);
|
|
111
|
+
}
|
|
112
|
+
return hasher.digest("binary");
|
|
113
|
+
}
|
|
106
114
|
function blake2b256(input) {
|
|
107
115
|
return blake2_js.blake2b(input, { dkLen: 32 });
|
|
108
116
|
}
|
|
@@ -158,190 +166,325 @@ function mthRecursive(leaves, start, end) {
|
|
|
158
166
|
const right = mthRecursive(leaves, start + k, end);
|
|
159
167
|
return hashNode(left, right);
|
|
160
168
|
}
|
|
161
|
-
|
|
162
|
-
zod.z.
|
|
163
|
-
|
|
164
|
-
})
|
|
165
|
-
|
|
166
|
-
var UTF8_ENCODER = new TextEncoder();
|
|
167
|
-
var UriChunkArraySchema = zod.z.array(
|
|
168
|
-
zod.z.string().refine(
|
|
169
|
-
(s) => {
|
|
170
|
-
const n = UTF8_ENCODER.encode(s).length;
|
|
171
|
-
return n >= 1 && n <= 64;
|
|
172
|
-
},
|
|
173
|
-
{ params: { code: "CHUNK_TOO_LARGE" } }
|
|
174
|
-
)
|
|
175
|
-
).min(1);
|
|
169
|
+
function textKeyedMap(inner) {
|
|
170
|
+
return zod.z.custom((value) => !(value instanceof Uint8Array), {
|
|
171
|
+
message: "CBOR byte string present where a text-keyed map is required"
|
|
172
|
+
}).pipe(inner);
|
|
173
|
+
}
|
|
176
174
|
var HashDigestSchema = zod.z.instanceof(Uint8Array);
|
|
177
175
|
var HashesMapSchema = zod.z.record(zod.z.string(), HashDigestSchema);
|
|
178
|
-
var
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
zod.z.
|
|
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
|
-
|
|
176
|
+
var UriSchema = zod.z.string();
|
|
177
|
+
var MerkleCommitSchema = textKeyedMap(
|
|
178
|
+
zod.z.object({
|
|
179
|
+
alg: zod.z.string(),
|
|
180
|
+
root: zod.z.instanceof(Uint8Array),
|
|
181
|
+
leaf_count: zod.z.union([zod.z.number().int(), zod.z.bigint()]),
|
|
182
|
+
uris: zod.z.array(UriSchema).optional()
|
|
183
|
+
}).strict()
|
|
184
|
+
);
|
|
185
|
+
var SlotSchema = textKeyedMap(
|
|
186
|
+
zod.z.object({
|
|
187
|
+
epk: zod.z.instanceof(Uint8Array).optional(),
|
|
188
|
+
kem_ct: zod.z.instanceof(Uint8Array).optional(),
|
|
189
|
+
wrap: zod.z.instanceof(Uint8Array).optional()
|
|
190
|
+
})
|
|
191
|
+
);
|
|
192
|
+
textKeyedMap(
|
|
193
|
+
zod.z.object({
|
|
194
|
+
m: zod.z.union([zod.z.number().int(), zod.z.bigint()]),
|
|
195
|
+
t: zod.z.union([zod.z.number().int(), zod.z.bigint()]),
|
|
196
|
+
p: zod.z.union([zod.z.number().int(), zod.z.bigint()])
|
|
197
|
+
}).strict()
|
|
198
|
+
);
|
|
199
|
+
var PassphraseBlockSchema = textKeyedMap(
|
|
200
|
+
zod.z.object({
|
|
201
|
+
alg: zod.z.string(),
|
|
202
|
+
salt: zod.z.instanceof(Uint8Array).superRefine((bytes, ctx) => {
|
|
203
|
+
if (bytes.length < 16) {
|
|
204
|
+
ctx.addIssue({
|
|
205
|
+
code: "custom",
|
|
206
|
+
path: [],
|
|
207
|
+
message: `passphrase.salt length ${bytes.length} < 16`,
|
|
208
|
+
params: { code: "ENC_PASSPHRASE_SALT_TOO_SHORT" }
|
|
209
|
+
});
|
|
210
|
+
} else if (bytes.length > 64) {
|
|
211
|
+
ctx.addIssue({
|
|
212
|
+
code: "custom",
|
|
213
|
+
path: [],
|
|
214
|
+
message: `passphrase.salt length ${bytes.length} > 64`,
|
|
215
|
+
params: { code: "ENC_PASSPHRASE_SALT_TOO_LONG" }
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
}),
|
|
219
|
+
params: zod.z.record(zod.z.string(), zod.z.unknown())
|
|
220
|
+
}).strict()
|
|
221
|
+
);
|
|
222
|
+
var EncScheme1Schema = textKeyedMap(
|
|
223
|
+
zod.z.object({
|
|
224
|
+
scheme: zod.z.literal(1),
|
|
225
|
+
aead: zod.z.string(),
|
|
226
|
+
kem: zod.z.string().optional(),
|
|
227
|
+
nonce: zod.z.instanceof(Uint8Array),
|
|
228
|
+
slots: zod.z.array(SlotSchema).optional(),
|
|
229
|
+
slots_mac: zod.z.instanceof(Uint8Array).refine((b) => b.length === 32, {
|
|
230
|
+
params: { code: "ENC_SLOTS_MAC_INVALID_LENGTH" }
|
|
231
|
+
}).optional(),
|
|
232
|
+
passphrase: PassphraseBlockSchema.optional()
|
|
233
|
+
}).strict()
|
|
234
|
+
);
|
|
235
|
+
var EncOpaqueSchema = textKeyedMap(
|
|
236
|
+
zod.z.looseObject({
|
|
237
|
+
scheme: zod.z.union([zod.z.number().int().nonnegative(), zod.z.bigint().nonnegative()])
|
|
238
|
+
})
|
|
239
|
+
);
|
|
240
|
+
zod.z.union([EncScheme1Schema, EncOpaqueSchema]);
|
|
241
|
+
var ItemEntrySchema = textKeyedMap(
|
|
242
|
+
zod.z.object({
|
|
243
|
+
hashes: HashesMapSchema,
|
|
244
|
+
uris: zod.z.array(UriSchema).optional(),
|
|
245
|
+
// Captured as `unknown`: the envelope is a union whose disposition
|
|
246
|
+
// (typed scheme-1 vs opaque) depends on identifier support, so the
|
|
247
|
+
// validator's domain pass — not this schema — narrows it.
|
|
248
|
+
enc: zod.z.unknown().optional()
|
|
249
|
+
}).strict()
|
|
250
|
+
);
|
|
251
|
+
var SigEntrySchema = textKeyedMap(
|
|
252
|
+
zod.z.object({
|
|
253
|
+
cose_key: zod.z.instanceof(Uint8Array).optional(),
|
|
254
|
+
cose_sign1: zod.z.instanceof(Uint8Array)
|
|
255
|
+
}).strict()
|
|
256
|
+
);
|
|
238
257
|
var SupersedesSchema = zod.z.instanceof(Uint8Array).refine((b) => b.length === 32, {
|
|
239
258
|
params: { code: "SUPERSEDES_TX_INVALID_LENGTH" }
|
|
240
259
|
});
|
|
241
260
|
var VersionLiteralSchema = zod.z.literal(1);
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
261
|
+
textKeyedMap(
|
|
262
|
+
zod.z.looseObject({
|
|
263
|
+
v: VersionLiteralSchema,
|
|
264
|
+
items: zod.z.array(ItemEntrySchema).optional(),
|
|
265
|
+
merkle: zod.z.array(MerkleCommitSchema).optional(),
|
|
266
|
+
supersedes: SupersedesSchema.optional(),
|
|
267
|
+
sigs: zod.z.array(SigEntrySchema).optional(),
|
|
268
|
+
crit: zod.z.array(zod.z.string()).optional()
|
|
269
|
+
})
|
|
270
|
+
);
|
|
250
271
|
|
|
251
272
|
// ../poe-standard/src/encoder.ts
|
|
252
273
|
function encodePoeRecord(record) {
|
|
253
|
-
return encodeCanonicalCbor(
|
|
254
|
-
}
|
|
255
|
-
function encodeRecordBodyForSigning(record) {
|
|
256
|
-
const body = recordToCborInternal(
|
|
274
|
+
return encodeCanonicalCbor(toCborValue(
|
|
257
275
|
record,
|
|
258
276
|
/* includeSigs */
|
|
259
|
-
|
|
260
|
-
);
|
|
261
|
-
return encodeCanonicalCbor(body);
|
|
277
|
+
true
|
|
278
|
+
));
|
|
262
279
|
}
|
|
263
|
-
function
|
|
264
|
-
return
|
|
280
|
+
function encodeRecordBodyForSigning(record) {
|
|
281
|
+
return encodeCanonicalCbor(toCborValue(
|
|
265
282
|
record,
|
|
266
283
|
/* includeSigs */
|
|
267
|
-
|
|
268
|
-
);
|
|
269
|
-
}
|
|
270
|
-
function recordToCborInternal(record, includeSigs) {
|
|
271
|
-
const out = { v: record.v };
|
|
272
|
-
if (record.items !== void 0) out["items"] = record.items.map(itemToCbor);
|
|
273
|
-
if (record.merkle !== void 0) out["merkle"] = record.merkle.map(merkleToCbor);
|
|
274
|
-
if (record.supersedes !== void 0) out["supersedes"] = record.supersedes;
|
|
275
|
-
if (includeSigs && record.sigs !== void 0) out["sigs"] = record.sigs.map(sigEntryToCbor);
|
|
276
|
-
if (record.crit !== void 0) out["crit"] = record.crit.slice();
|
|
277
|
-
for (const [k, v] of Object.entries(record)) {
|
|
278
|
-
if (k === "v" || k === "items" || k === "merkle" || k === "supersedes" || k === "sigs" || k === "crit") {
|
|
279
|
-
continue;
|
|
280
|
-
}
|
|
281
|
-
out[k] = v;
|
|
282
|
-
}
|
|
283
|
-
return out;
|
|
284
|
-
}
|
|
285
|
-
function itemToCbor(item) {
|
|
286
|
-
const out = { hashes: hashesToCbor(item.hashes) };
|
|
287
|
-
if (item.uris !== void 0) {
|
|
288
|
-
out["uris"] = item.uris.map((chunks) => chunks.slice());
|
|
289
|
-
}
|
|
290
|
-
if (item.enc !== void 0) {
|
|
291
|
-
out["enc"] = envelopeToCbor(item.enc);
|
|
292
|
-
}
|
|
293
|
-
return out;
|
|
284
|
+
false
|
|
285
|
+
));
|
|
294
286
|
}
|
|
295
|
-
function
|
|
287
|
+
function toCborValue(record, includeSigs) {
|
|
296
288
|
const out = {};
|
|
297
|
-
for (const [
|
|
298
|
-
|
|
289
|
+
for (const [key, value] of Object.entries(record)) {
|
|
290
|
+
if (value === void 0) continue;
|
|
291
|
+
if (!includeSigs && key === "sigs") continue;
|
|
292
|
+
out[key] = stripUndefined(value);
|
|
299
293
|
}
|
|
300
294
|
return out;
|
|
301
295
|
}
|
|
302
|
-
function
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
root: commit.root,
|
|
306
|
-
leaf_count: commit.leaf_count
|
|
307
|
-
};
|
|
308
|
-
if (commit.uris !== void 0) {
|
|
309
|
-
out["uris"] = commit.uris.map((chunks) => chunks.slice());
|
|
296
|
+
function stripUndefined(value) {
|
|
297
|
+
if (value === null || typeof value !== "object" || value instanceof Uint8Array) {
|
|
298
|
+
return value;
|
|
310
299
|
}
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
function envelopeToCbor(enc) {
|
|
314
|
-
const out = {
|
|
315
|
-
scheme: enc.scheme,
|
|
316
|
-
aead: enc.aead,
|
|
317
|
-
nonce: enc.nonce
|
|
318
|
-
};
|
|
319
|
-
if (enc.kem !== void 0) out["kem"] = enc.kem;
|
|
320
|
-
if (enc.slots !== void 0) out["slots"] = enc.slots.map(slotToCbor);
|
|
321
|
-
if (enc.slots_mac !== void 0) out["slots_mac"] = enc.slots_mac;
|
|
322
|
-
if (enc.passphrase !== void 0) out["passphrase"] = passphraseToCbor(enc.passphrase);
|
|
323
|
-
return out;
|
|
324
|
-
}
|
|
325
|
-
function slotToCbor(slot) {
|
|
326
|
-
if (slot.kem_ct !== void 0) {
|
|
327
|
-
return { kem_ct: slot.kem_ct.map((c) => c), wrap: slot.wrap };
|
|
300
|
+
if (Array.isArray(value)) {
|
|
301
|
+
return value.map((element) => stripUndefined(element));
|
|
328
302
|
}
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
out["cose_key"] = entry.cose_key.map((b) => b);
|
|
303
|
+
if (value instanceof Map) {
|
|
304
|
+
const out2 = /* @__PURE__ */ new Map();
|
|
305
|
+
for (const [k, v] of value) {
|
|
306
|
+
if (v === void 0) continue;
|
|
307
|
+
out2.set(k, stripUndefined(v));
|
|
308
|
+
}
|
|
309
|
+
return out2;
|
|
310
|
+
}
|
|
311
|
+
const out = {};
|
|
312
|
+
for (const [k, v] of Object.entries(value)) {
|
|
313
|
+
if (v === void 0) continue;
|
|
314
|
+
out[k] = stripUndefined(v);
|
|
342
315
|
}
|
|
343
316
|
return out;
|
|
344
317
|
}
|
|
318
|
+
|
|
319
|
+
// ../poe-standard/src/error-codes.ts
|
|
320
|
+
var ERROR_CODES = [
|
|
321
|
+
"MALFORMED_CBOR",
|
|
322
|
+
"SCHEMA_TYPE_MISMATCH",
|
|
323
|
+
"SCHEMA_MISSING_REQUIRED",
|
|
324
|
+
"SCHEMA_UNKNOWN_FIELD",
|
|
325
|
+
"SCHEMA_INVALID_LITERAL",
|
|
326
|
+
"SCHEMA_EMPTY_RECORD",
|
|
327
|
+
"HASH_DIGEST_LENGTH_MISMATCH",
|
|
328
|
+
"UNSUPPORTED_HASH_ALG",
|
|
329
|
+
"UNSUPPORTED_MERKLE_COMMIT_ALG",
|
|
330
|
+
"SCHEMA_MERKLE_LEAF_COUNT_INVALID",
|
|
331
|
+
"INVALID_URI",
|
|
332
|
+
"CHUNK_TOO_LARGE",
|
|
333
|
+
"UNAUTHENTICATED_CIPHER_FORBIDDEN",
|
|
334
|
+
"UNSUPPORTED_AEAD_ALG",
|
|
335
|
+
"NONCE_LENGTH_MISMATCH",
|
|
336
|
+
"UNSUPPORTED_ENVELOPE_SCHEME",
|
|
337
|
+
"ENC_UNSUPPORTED",
|
|
338
|
+
"ENC_SLOTS_EMPTY",
|
|
339
|
+
"ENC_SLOT_INVALID_SHAPE",
|
|
340
|
+
"UNSUPPORTED_KEM_ALG",
|
|
341
|
+
"ENC_KEM_REQUIRED",
|
|
342
|
+
"KEM_EPK_LENGTH_MISMATCH",
|
|
343
|
+
"KEM_CT_LENGTH_MISMATCH",
|
|
344
|
+
"WRAP_LENGTH_MISMATCH",
|
|
345
|
+
"ENC_SLOTS_MAC_INVALID_LENGTH",
|
|
346
|
+
"ENC_SLOTS_MAC_REQUIRED",
|
|
347
|
+
"ENC_SLOTS_REQUIRED",
|
|
348
|
+
"ENC_SLOTS_DUPLICATE_KEM_MATERIAL",
|
|
349
|
+
"ENC_SLOTS_TOO_MANY",
|
|
350
|
+
"ENC_ENVELOPE_TOO_LARGE",
|
|
351
|
+
"ENC_EXCLUSIVITY_VIOLATION",
|
|
352
|
+
"ENC_NO_KEY_PATH",
|
|
353
|
+
"ENC_REQUIRES_CONTENT_HASH",
|
|
354
|
+
"ENC_PASSPHRASE_ALG_UNSUPPORTED",
|
|
355
|
+
"ENC_PASSPHRASE_SALT_TOO_SHORT",
|
|
356
|
+
"ENC_PASSPHRASE_SALT_TOO_LONG",
|
|
357
|
+
"ENC_PASSPHRASE_ARGON2_PARAMS_TOO_LOW",
|
|
358
|
+
"ENC_PASSPHRASE_PARAMS_EXCEED_POLICY",
|
|
359
|
+
"MALFORMED_SIG_COSE_SIGN1",
|
|
360
|
+
"SIGNATURE_UNSUPPORTED",
|
|
361
|
+
"SIG_ENTRY_INVALID_SHAPE",
|
|
362
|
+
"SIG_ENTRY_KID_COSE_KEY_CONFLICT",
|
|
363
|
+
"SIG_PRIVATE_KEY_LEAKED",
|
|
364
|
+
"SUPERSEDES_TX_INVALID_LENGTH",
|
|
365
|
+
"EXTENSION_UNSUPPORTED_CRITICAL",
|
|
366
|
+
"CRIT_SHAPE_INVALID",
|
|
367
|
+
"TX_NOT_FOUND",
|
|
368
|
+
"PROVIDER_UNAVAILABLE",
|
|
369
|
+
"TX_INTEGRITY_MISMATCH",
|
|
370
|
+
"METADATA_NOT_FOUND",
|
|
371
|
+
"INSUFFICIENT_CONFIRMATIONS",
|
|
372
|
+
"SIGNATURE_INVALID",
|
|
373
|
+
"SIGNER_KEY_UNRESOLVED",
|
|
374
|
+
"WALLET_ADDRESS_MISMATCH",
|
|
375
|
+
"URI_TARGET_FORBIDDEN",
|
|
376
|
+
"URI_INTEGRITY_MISMATCH",
|
|
377
|
+
"URI_PROVIDER_INTEGRITY_MISMATCH",
|
|
378
|
+
"URI_FETCH_FAILED",
|
|
379
|
+
"CONTENT_UNAVAILABLE",
|
|
380
|
+
"CONTENT_FETCH_LIMIT_EXCEEDED",
|
|
381
|
+
"CIPHERTEXT_UNAVAILABLE",
|
|
382
|
+
"SERVICE_INDEPENDENCE_VIOLATION",
|
|
383
|
+
"WRONG_DECRYPTION_INPUT_SHAPE",
|
|
384
|
+
"WRONG_RECIPIENT_KEY",
|
|
385
|
+
"TAMPERED_HEADER",
|
|
386
|
+
"TAMPERED_CIPHERTEXT",
|
|
387
|
+
"KDF_DERIVATION_FAILED",
|
|
388
|
+
"ENC_PASSPHRASE_UNNORMALIZABLE",
|
|
389
|
+
"ENC_PASSPHRASE_EMPTY",
|
|
390
|
+
"SCHEMA_MERKLE_LEAF_COUNT_MISMATCH",
|
|
391
|
+
"SCHEMA_MERKLE_LEAVES_FORMAT_UNSUPPORTED",
|
|
392
|
+
"SCHEMA_MERKLE_LEAVES_MALFORMED",
|
|
393
|
+
"MERKLE_ROOT_MISMATCH",
|
|
394
|
+
"MERKLE_LEAVES_UNAVAILABLE",
|
|
395
|
+
"MERKLE_UNSUPPORTED",
|
|
396
|
+
"OUT_OF_PROFILE_SKIPPED"
|
|
397
|
+
];
|
|
398
|
+
var ERROR_CODE_PART = Object.freeze({
|
|
399
|
+
MALFORMED_CBOR: "A",
|
|
400
|
+
SCHEMA_TYPE_MISMATCH: "A",
|
|
401
|
+
SCHEMA_MISSING_REQUIRED: "A",
|
|
402
|
+
SCHEMA_UNKNOWN_FIELD: "A",
|
|
403
|
+
SCHEMA_INVALID_LITERAL: "A",
|
|
404
|
+
SCHEMA_EMPTY_RECORD: "A",
|
|
405
|
+
HASH_DIGEST_LENGTH_MISMATCH: "A",
|
|
406
|
+
UNSUPPORTED_HASH_ALG: "A",
|
|
407
|
+
UNSUPPORTED_MERKLE_COMMIT_ALG: "A",
|
|
408
|
+
SCHEMA_MERKLE_LEAF_COUNT_INVALID: "A",
|
|
409
|
+
INVALID_URI: "A",
|
|
410
|
+
CHUNK_TOO_LARGE: "carriage",
|
|
411
|
+
UNAUTHENTICATED_CIPHER_FORBIDDEN: "A",
|
|
412
|
+
UNSUPPORTED_AEAD_ALG: "A",
|
|
413
|
+
NONCE_LENGTH_MISMATCH: "A",
|
|
414
|
+
UNSUPPORTED_ENVELOPE_SCHEME: "A",
|
|
415
|
+
ENC_UNSUPPORTED: "A",
|
|
416
|
+
ENC_SLOTS_EMPTY: "A",
|
|
417
|
+
ENC_SLOT_INVALID_SHAPE: "A",
|
|
418
|
+
UNSUPPORTED_KEM_ALG: "A",
|
|
419
|
+
ENC_KEM_REQUIRED: "A",
|
|
420
|
+
KEM_EPK_LENGTH_MISMATCH: "A",
|
|
421
|
+
KEM_CT_LENGTH_MISMATCH: "A",
|
|
422
|
+
WRAP_LENGTH_MISMATCH: "A",
|
|
423
|
+
ENC_SLOTS_MAC_INVALID_LENGTH: "A",
|
|
424
|
+
ENC_SLOTS_MAC_REQUIRED: "A",
|
|
425
|
+
ENC_SLOTS_REQUIRED: "A",
|
|
426
|
+
ENC_SLOTS_DUPLICATE_KEM_MATERIAL: "A",
|
|
427
|
+
ENC_SLOTS_TOO_MANY: "A",
|
|
428
|
+
ENC_ENVELOPE_TOO_LARGE: "A",
|
|
429
|
+
ENC_EXCLUSIVITY_VIOLATION: "A",
|
|
430
|
+
ENC_NO_KEY_PATH: "A",
|
|
431
|
+
ENC_REQUIRES_CONTENT_HASH: "A",
|
|
432
|
+
ENC_PASSPHRASE_ALG_UNSUPPORTED: "A",
|
|
433
|
+
ENC_PASSPHRASE_SALT_TOO_SHORT: "A",
|
|
434
|
+
ENC_PASSPHRASE_SALT_TOO_LONG: "A",
|
|
435
|
+
ENC_PASSPHRASE_ARGON2_PARAMS_TOO_LOW: "A",
|
|
436
|
+
ENC_PASSPHRASE_PARAMS_EXCEED_POLICY: "A",
|
|
437
|
+
MALFORMED_SIG_COSE_SIGN1: "A",
|
|
438
|
+
SIGNATURE_UNSUPPORTED: "A",
|
|
439
|
+
SIG_ENTRY_INVALID_SHAPE: "A",
|
|
440
|
+
SIG_ENTRY_KID_COSE_KEY_CONFLICT: "A",
|
|
441
|
+
SIG_PRIVATE_KEY_LEAKED: "A",
|
|
442
|
+
SUPERSEDES_TX_INVALID_LENGTH: "A",
|
|
443
|
+
EXTENSION_UNSUPPORTED_CRITICAL: "A",
|
|
444
|
+
CRIT_SHAPE_INVALID: "A",
|
|
445
|
+
TX_NOT_FOUND: "B",
|
|
446
|
+
PROVIDER_UNAVAILABLE: "B",
|
|
447
|
+
TX_INTEGRITY_MISMATCH: "B",
|
|
448
|
+
METADATA_NOT_FOUND: "B",
|
|
449
|
+
INSUFFICIENT_CONFIRMATIONS: "B",
|
|
450
|
+
SIGNATURE_INVALID: "B",
|
|
451
|
+
SIGNER_KEY_UNRESOLVED: "B",
|
|
452
|
+
WALLET_ADDRESS_MISMATCH: "B",
|
|
453
|
+
URI_TARGET_FORBIDDEN: "B",
|
|
454
|
+
URI_INTEGRITY_MISMATCH: "B",
|
|
455
|
+
URI_PROVIDER_INTEGRITY_MISMATCH: "B",
|
|
456
|
+
URI_FETCH_FAILED: "B",
|
|
457
|
+
CONTENT_UNAVAILABLE: "B",
|
|
458
|
+
CONTENT_FETCH_LIMIT_EXCEEDED: "B",
|
|
459
|
+
CIPHERTEXT_UNAVAILABLE: "B",
|
|
460
|
+
SERVICE_INDEPENDENCE_VIOLATION: "B",
|
|
461
|
+
WRONG_DECRYPTION_INPUT_SHAPE: "B",
|
|
462
|
+
WRONG_RECIPIENT_KEY: "B",
|
|
463
|
+
TAMPERED_HEADER: "B",
|
|
464
|
+
TAMPERED_CIPHERTEXT: "B",
|
|
465
|
+
KDF_DERIVATION_FAILED: "B",
|
|
466
|
+
ENC_PASSPHRASE_UNNORMALIZABLE: "B",
|
|
467
|
+
ENC_PASSPHRASE_EMPTY: "B",
|
|
468
|
+
SCHEMA_MERKLE_LEAF_COUNT_MISMATCH: "B",
|
|
469
|
+
SCHEMA_MERKLE_LEAVES_FORMAT_UNSUPPORTED: "B",
|
|
470
|
+
SCHEMA_MERKLE_LEAVES_MALFORMED: "B",
|
|
471
|
+
MERKLE_ROOT_MISMATCH: "B",
|
|
472
|
+
MERKLE_LEAVES_UNAVAILABLE: "B",
|
|
473
|
+
MERKLE_UNSUPPORTED: "B",
|
|
474
|
+
OUT_OF_PROFILE_SKIPPED: "B"
|
|
475
|
+
});
|
|
476
|
+
Object.freeze(
|
|
477
|
+
ERROR_CODES.filter((code) => ERROR_CODE_PART[code] === "A")
|
|
478
|
+
);
|
|
479
|
+
Object.freeze(
|
|
480
|
+
ERROR_CODES.filter((code) => ERROR_CODE_PART[code] === "carriage")
|
|
481
|
+
);
|
|
482
|
+
Object.freeze(
|
|
483
|
+
ERROR_CODES.filter((code) => ERROR_CODE_PART[code] === "B")
|
|
484
|
+
);
|
|
485
|
+
new Map(
|
|
486
|
+
ERROR_CODES.map((code, index) => [code, index])
|
|
487
|
+
);
|
|
345
488
|
var abytesDoc = utils_js$1.abytes;
|
|
346
489
|
var randomBytes = utils_js$1.randomBytes;
|
|
347
490
|
function equalBytes(a, b) {
|
|
@@ -1006,12 +1149,6 @@ var XWing = /* @__PURE__ */ (() => ml_kem768_x25519)();
|
|
|
1006
1149
|
function chacha20Poly1305Encrypt(opts2) {
|
|
1007
1150
|
return chacha_js.chacha20poly1305(opts2.key, opts2.nonce, opts2.aad).encrypt(opts2.plaintext);
|
|
1008
1151
|
}
|
|
1009
|
-
function xchacha20Poly1305Encrypt(opts2) {
|
|
1010
|
-
return chacha_js.xchacha20poly1305(opts2.key, opts2.nonce, opts2.aad).encrypt(opts2.plaintext);
|
|
1011
|
-
}
|
|
1012
|
-
function hkdfSha256(opts2) {
|
|
1013
|
-
return hkdf_js.hkdf(sha2_js.sha256, opts2.ikm, opts2.salt, opts2.info, opts2.length);
|
|
1014
|
-
}
|
|
1015
1152
|
var MLKEM768X25519_PUBLIC_KEY_LENGTH = 1216;
|
|
1016
1153
|
var MLKEM768X25519_ENC_LENGTH = 1120;
|
|
1017
1154
|
var MLKEM768X25519_ESEED_LENGTH = 64;
|
|
@@ -1050,6 +1187,9 @@ function x25519Ecdh(opts2) {
|
|
|
1050
1187
|
throw e;
|
|
1051
1188
|
}
|
|
1052
1189
|
}
|
|
1190
|
+
function hkdfSha256(opts2) {
|
|
1191
|
+
return hkdf_js.hkdf(sha2_js.sha256, opts2.ikm, opts2.salt, opts2.info, opts2.length);
|
|
1192
|
+
}
|
|
1053
1193
|
var EciesSealedPoeError = class extends Error {
|
|
1054
1194
|
code;
|
|
1055
1195
|
constructor(code, message, options) {
|
|
@@ -1058,37 +1198,98 @@ var EciesSealedPoeError = class extends Error {
|
|
|
1058
1198
|
this.code = code;
|
|
1059
1199
|
}
|
|
1060
1200
|
};
|
|
1061
|
-
var
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1201
|
+
var CHUNK_SIZE = 65536;
|
|
1202
|
+
var TAG_SIZE = 16;
|
|
1203
|
+
var NONCE_LENGTH = 12;
|
|
1204
|
+
var COUNTER_LENGTH = 11;
|
|
1205
|
+
var PAYLOAD_KEY_LENGTH = 32;
|
|
1206
|
+
var EMPTY_AAD = new Uint8Array(0);
|
|
1207
|
+
var ChunkNonce = class {
|
|
1208
|
+
nonce = new Uint8Array(NONCE_LENGTH);
|
|
1209
|
+
finished = false;
|
|
1210
|
+
next(final) {
|
|
1211
|
+
if (this.finished) {
|
|
1212
|
+
throw new Error("STREAM: no chunks may follow the final chunk");
|
|
1213
|
+
}
|
|
1214
|
+
if (final) {
|
|
1215
|
+
this.finished = true;
|
|
1216
|
+
this.nonce[COUNTER_LENGTH] = 1;
|
|
1217
|
+
}
|
|
1218
|
+
const out = this.nonce.slice();
|
|
1219
|
+
this.increment();
|
|
1220
|
+
return out;
|
|
1221
|
+
}
|
|
1222
|
+
get done() {
|
|
1223
|
+
return this.finished;
|
|
1224
|
+
}
|
|
1225
|
+
increment() {
|
|
1226
|
+
for (let i = COUNTER_LENGTH - 1; i >= 0; i--) {
|
|
1227
|
+
const v = this.nonce[i] + 1 & 255;
|
|
1228
|
+
this.nonce[i] = v;
|
|
1229
|
+
if (v !== 0) return;
|
|
1230
|
+
}
|
|
1231
|
+
throw new Error("STREAM: chunk counter overflow");
|
|
1065
1232
|
}
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1233
|
+
};
|
|
1234
|
+
function assertPayloadKey(payloadKey) {
|
|
1235
|
+
if (payloadKey.length !== PAYLOAD_KEY_LENGTH) {
|
|
1236
|
+
throw new Error(
|
|
1237
|
+
`STREAM: payloadKey MUST be exactly ${PAYLOAD_KEY_LENGTH} bytes, got ${payloadKey.length}`
|
|
1238
|
+
);
|
|
1069
1239
|
}
|
|
1070
|
-
return chunks;
|
|
1071
1240
|
}
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1241
|
+
var StreamSealer = class {
|
|
1242
|
+
payloadKey;
|
|
1243
|
+
nonce = new ChunkNonce();
|
|
1244
|
+
chunkIndex = 0;
|
|
1245
|
+
constructor(payloadKey) {
|
|
1246
|
+
assertPayloadKey(payloadKey);
|
|
1247
|
+
this.payloadKey = payloadKey;
|
|
1248
|
+
}
|
|
1249
|
+
sealChunk(plaintext, final) {
|
|
1250
|
+
if (!final && plaintext.length !== CHUNK_SIZE) {
|
|
1251
|
+
throw new Error(
|
|
1252
|
+
`STREAM: non-final chunk MUST carry exactly ${CHUNK_SIZE} plaintext bytes, got ${plaintext.length}`
|
|
1253
|
+
);
|
|
1254
|
+
}
|
|
1255
|
+
if (final && plaintext.length > CHUNK_SIZE) {
|
|
1256
|
+
throw new Error(
|
|
1257
|
+
`STREAM: final chunk MUST carry at most ${CHUNK_SIZE} plaintext bytes, got ${plaintext.length}`
|
|
1258
|
+
);
|
|
1259
|
+
}
|
|
1260
|
+
if (final && plaintext.length === 0 && this.chunkIndex > 0) {
|
|
1261
|
+
throw new Error(
|
|
1262
|
+
"STREAM: a zero-length final chunk is admissible only for an empty plaintext"
|
|
1263
|
+
);
|
|
1264
|
+
}
|
|
1265
|
+
const sealed = chacha20Poly1305Encrypt({
|
|
1266
|
+
key: this.payloadKey,
|
|
1267
|
+
nonce: this.nonce.next(final),
|
|
1268
|
+
aad: EMPTY_AAD,
|
|
1269
|
+
plaintext
|
|
1270
|
+
});
|
|
1271
|
+
this.chunkIndex += 1;
|
|
1272
|
+
return sealed;
|
|
1273
|
+
}
|
|
1274
|
+
};
|
|
1275
|
+
function streamSeal(args) {
|
|
1276
|
+
const { plaintext } = args;
|
|
1277
|
+
const sealer = new StreamSealer(args.payloadKey);
|
|
1278
|
+
const chunkCount = Math.max(1, Math.ceil(plaintext.length / CHUNK_SIZE));
|
|
1279
|
+
const out = new Uint8Array(plaintext.length + chunkCount * TAG_SIZE);
|
|
1076
1280
|
let offset = 0;
|
|
1077
|
-
for (
|
|
1078
|
-
|
|
1079
|
-
|
|
1281
|
+
for (let i = 0; i < chunkCount; i++) {
|
|
1282
|
+
const final = i === chunkCount - 1;
|
|
1283
|
+
const chunk = plaintext.subarray(
|
|
1284
|
+
i * CHUNK_SIZE,
|
|
1285
|
+
Math.min((i + 1) * CHUNK_SIZE, plaintext.length)
|
|
1286
|
+
);
|
|
1287
|
+
const sealed = sealer.sealChunk(chunk, final);
|
|
1288
|
+
out.set(sealed, offset);
|
|
1289
|
+
offset += sealed.length;
|
|
1080
1290
|
}
|
|
1081
1291
|
return out;
|
|
1082
1292
|
}
|
|
1083
|
-
function canonicalizeSlots(slots, kem) {
|
|
1084
|
-
if (kem === "x25519") {
|
|
1085
|
-
return slots.map((s) => ({ epk: s.epk, wrap: s.wrap }));
|
|
1086
|
-
}
|
|
1087
|
-
return slots.map((s) => ({
|
|
1088
|
-
kem_ct: chunkKemCt(joinKemCt(s.kem_ct)),
|
|
1089
|
-
wrap: s.wrap
|
|
1090
|
-
}));
|
|
1091
|
-
}
|
|
1092
1293
|
function encodeCanonicalCbor3(value) {
|
|
1093
1294
|
return cbor2.encode(value, {
|
|
1094
1295
|
cde: true,
|
|
@@ -1097,23 +1298,54 @@ function encodeCanonicalCbor3(value) {
|
|
|
1097
1298
|
sortKeys: sorts.sortCoreDeterministic
|
|
1098
1299
|
});
|
|
1099
1300
|
}
|
|
1301
|
+
var CARDANO_POE_ITEM_HASHES_PREFIX = new TextEncoder().encode(
|
|
1302
|
+
"cardano-poe-item-hashes-v1"
|
|
1303
|
+
);
|
|
1100
1304
|
var CARDANO_POE_SLOTS_TRANSCRIPT_PREFIX = new TextEncoder().encode(
|
|
1101
1305
|
"cardano-poe-slots-transcript-v1"
|
|
1102
1306
|
);
|
|
1307
|
+
var CARDANO_POE_PASSPHRASE_TRANSCRIPT_PREFIX = new TextEncoder().encode(
|
|
1308
|
+
"cardano-poe-passphrase-transcript-v1"
|
|
1309
|
+
);
|
|
1310
|
+
var CARDANO_POE_HKDF_INFO_SLOTS_MAC = new TextEncoder().encode(
|
|
1311
|
+
"cardano-poe-slots-mac-v1"
|
|
1312
|
+
);
|
|
1313
|
+
var CARDANO_POE_HKDF_INFO_PASSPHRASE_MAC = new TextEncoder().encode(
|
|
1314
|
+
"cardano-poe-passphrase-mac-v1"
|
|
1315
|
+
);
|
|
1103
1316
|
var CARDANO_POE_HKDF_INFO_PAYLOAD = new TextEncoder().encode(
|
|
1104
1317
|
"cardano-poe-payload-v1"
|
|
1105
1318
|
);
|
|
1106
1319
|
var CARDANO_POE_HKDF_INFO_PAYLOAD_PASSPHRASE = new TextEncoder().encode(
|
|
1107
1320
|
"cardano-poe-payload-passphrase-v1"
|
|
1108
1321
|
);
|
|
1322
|
+
var CARDANO_POE_X25519_KEK_SALT_PREFIX = new TextEncoder().encode(
|
|
1323
|
+
"cardano-poe-x25519-kek-salt-v1"
|
|
1324
|
+
);
|
|
1109
1325
|
var CARDANO_POE_XWING_KEK_SALT_PREFIX = new TextEncoder().encode(
|
|
1110
1326
|
"cardano-poe-xwing-kek-salt-v1"
|
|
1111
1327
|
);
|
|
1328
|
+
if (CARDANO_POE_ITEM_HASHES_PREFIX.length !== 26) {
|
|
1329
|
+
throw new Error("CARDANO_POE_ITEM_HASHES_PREFIX byte-length invariant violated (expected 26)");
|
|
1330
|
+
}
|
|
1112
1331
|
if (CARDANO_POE_SLOTS_TRANSCRIPT_PREFIX.length !== 31) {
|
|
1113
1332
|
throw new Error(
|
|
1114
1333
|
"CARDANO_POE_SLOTS_TRANSCRIPT_PREFIX byte-length invariant violated (expected 31)"
|
|
1115
1334
|
);
|
|
1116
1335
|
}
|
|
1336
|
+
if (CARDANO_POE_PASSPHRASE_TRANSCRIPT_PREFIX.length !== 36) {
|
|
1337
|
+
throw new Error(
|
|
1338
|
+
"CARDANO_POE_PASSPHRASE_TRANSCRIPT_PREFIX byte-length invariant violated (expected 36)"
|
|
1339
|
+
);
|
|
1340
|
+
}
|
|
1341
|
+
if (CARDANO_POE_HKDF_INFO_SLOTS_MAC.length !== 24) {
|
|
1342
|
+
throw new Error("CARDANO_POE_HKDF_INFO_SLOTS_MAC byte-length invariant violated (expected 24)");
|
|
1343
|
+
}
|
|
1344
|
+
if (CARDANO_POE_HKDF_INFO_PASSPHRASE_MAC.length !== 29) {
|
|
1345
|
+
throw new Error(
|
|
1346
|
+
"CARDANO_POE_HKDF_INFO_PASSPHRASE_MAC byte-length invariant violated (expected 29)"
|
|
1347
|
+
);
|
|
1348
|
+
}
|
|
1117
1349
|
if (CARDANO_POE_HKDF_INFO_PAYLOAD.length !== 22) {
|
|
1118
1350
|
throw new Error("CARDANO_POE_HKDF_INFO_PAYLOAD byte-length invariant violated (expected 22)");
|
|
1119
1351
|
}
|
|
@@ -1122,49 +1354,60 @@ if (CARDANO_POE_HKDF_INFO_PAYLOAD_PASSPHRASE.length !== 33) {
|
|
|
1122
1354
|
"CARDANO_POE_HKDF_INFO_PAYLOAD_PASSPHRASE byte-length invariant violated (expected 33)"
|
|
1123
1355
|
);
|
|
1124
1356
|
}
|
|
1357
|
+
if (CARDANO_POE_X25519_KEK_SALT_PREFIX.length !== 30) {
|
|
1358
|
+
throw new Error(
|
|
1359
|
+
"CARDANO_POE_X25519_KEK_SALT_PREFIX byte-length invariant violated (expected 30)"
|
|
1360
|
+
);
|
|
1361
|
+
}
|
|
1125
1362
|
if (CARDANO_POE_XWING_KEK_SALT_PREFIX.length !== 29) {
|
|
1126
1363
|
throw new Error("CARDANO_POE_XWING_KEK_SALT_PREFIX byte-length invariant violated (expected 29)");
|
|
1127
1364
|
}
|
|
1128
|
-
var
|
|
1129
|
-
function
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1365
|
+
var EMPTY_SALT = new Uint8Array(0);
|
|
1366
|
+
function labelledSha256(prefix, ...parts) {
|
|
1367
|
+
let total = prefix.length;
|
|
1368
|
+
for (const p of parts) total += p.length;
|
|
1369
|
+
const message = new Uint8Array(total);
|
|
1370
|
+
message.set(prefix, 0);
|
|
1371
|
+
let offset = prefix.length;
|
|
1372
|
+
for (const p of parts) {
|
|
1373
|
+
message.set(p, offset);
|
|
1374
|
+
offset += p.length;
|
|
1134
1375
|
}
|
|
1376
|
+
return sha2_js.sha256(message);
|
|
1135
1377
|
}
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1378
|
+
function itemHashesHash(hashes2) {
|
|
1379
|
+
if (Object.keys(hashes2).length === 0) {
|
|
1380
|
+
throw new EciesSealedPoeError(
|
|
1381
|
+
"ENC_REQUIRES_CONTENT_HASH",
|
|
1382
|
+
"hashes MUST carry at least one content-hash entry"
|
|
1383
|
+
);
|
|
1140
1384
|
}
|
|
1141
|
-
|
|
1385
|
+
return labelledSha256(CARDANO_POE_ITEM_HASHES_PREFIX, encodeCanonicalCbor3(hashes2));
|
|
1386
|
+
}
|
|
1142
1387
|
function computeSlotsHash(args) {
|
|
1388
|
+
const slots = args.kem === "x25519" ? args.slots.map((s) => ({ epk: s.epk, wrap: s.wrap })) : args.slots.map((s) => ({
|
|
1389
|
+
kem_ct: s.kem_ct,
|
|
1390
|
+
wrap: s.wrap
|
|
1391
|
+
}));
|
|
1143
1392
|
const transcript = {
|
|
1144
1393
|
scheme: 1,
|
|
1145
1394
|
path: "slots",
|
|
1146
|
-
aead:
|
|
1395
|
+
aead: args.aead,
|
|
1147
1396
|
kem: args.kem,
|
|
1148
1397
|
nonce: args.nonce,
|
|
1149
|
-
slots
|
|
1398
|
+
slots,
|
|
1399
|
+
hashes_hash: args.hashesHash
|
|
1150
1400
|
};
|
|
1151
|
-
|
|
1152
|
-
const message = new Uint8Array(CARDANO_POE_SLOTS_TRANSCRIPT_PREFIX.length + encoded.length);
|
|
1153
|
-
message.set(CARDANO_POE_SLOTS_TRANSCRIPT_PREFIX, 0);
|
|
1154
|
-
message.set(encoded, CARDANO_POE_SLOTS_TRANSCRIPT_PREFIX.length);
|
|
1155
|
-
return sha2_js.sha256(message);
|
|
1401
|
+
return labelledSha256(CARDANO_POE_SLOTS_TRANSCRIPT_PREFIX, encodeCanonicalCbor3(transcript));
|
|
1156
1402
|
}
|
|
1157
|
-
function
|
|
1158
|
-
const
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
slots_mac: args.slotsMac
|
|
1166
|
-
};
|
|
1167
|
-
return encodeCanonicalCbor3(ad);
|
|
1403
|
+
function computeSlotsMac(args) {
|
|
1404
|
+
const macKey = hkdfSha256({
|
|
1405
|
+
ikm: args.cek,
|
|
1406
|
+
salt: EMPTY_SALT,
|
|
1407
|
+
info: CARDANO_POE_HKDF_INFO_SLOTS_MAC,
|
|
1408
|
+
length: 32
|
|
1409
|
+
});
|
|
1410
|
+
return hmac_js.hmac(sha2_js.sha256, macKey, args.slotsHash);
|
|
1168
1411
|
}
|
|
1169
1412
|
function slotsPayloadKey(args) {
|
|
1170
1413
|
return hkdfSha256({
|
|
@@ -1174,31 +1417,22 @@ function slotsPayloadKey(args) {
|
|
|
1174
1417
|
length: 32
|
|
1175
1418
|
});
|
|
1176
1419
|
}
|
|
1420
|
+
function x25519KekSalt(args) {
|
|
1421
|
+
return labelledSha256(CARDANO_POE_X25519_KEK_SALT_PREFIX, args.nonce, args.epk, args.pubR);
|
|
1422
|
+
}
|
|
1177
1423
|
function xwingKekSalt(args) {
|
|
1178
|
-
|
|
1179
|
-
CARDANO_POE_XWING_KEK_SALT_PREFIX.length + args.kemCt.length + args.pubR.length
|
|
1180
|
-
);
|
|
1181
|
-
let offset = 0;
|
|
1182
|
-
message.set(CARDANO_POE_XWING_KEK_SALT_PREFIX, offset);
|
|
1183
|
-
offset += CARDANO_POE_XWING_KEK_SALT_PREFIX.length;
|
|
1184
|
-
message.set(args.kemCt, offset);
|
|
1185
|
-
offset += args.kemCt.length;
|
|
1186
|
-
message.set(args.pubR, offset);
|
|
1187
|
-
return sha2_js.sha256(message);
|
|
1424
|
+
return labelledSha256(CARDANO_POE_XWING_KEK_SALT_PREFIX, args.nonce, args.kemCt, args.pubR);
|
|
1188
1425
|
}
|
|
1426
|
+
var SEALED_POE_AEAD = "chacha20-poly1305-stream64k";
|
|
1189
1427
|
var CARDANO_POE_HKDF_INFO_KEK = new TextEncoder().encode("cardano-poe-kek-v1");
|
|
1190
1428
|
var CARDANO_POE_HKDF_INFO_KEK_MLKEM768X25519 = new TextEncoder().encode(
|
|
1191
1429
|
"cardano-poe-kek-mlkem768x25519-v1"
|
|
1192
1430
|
);
|
|
1193
|
-
var CARDANO_POE_HKDF_INFO_SLOTS_MAC = new TextEncoder().encode(
|
|
1194
|
-
"cardano-poe-slots-mac-v1"
|
|
1195
|
-
);
|
|
1196
1431
|
var ZERO_NONCE_12 = new Uint8Array(12);
|
|
1197
|
-
var EMPTY_SALT = new Uint8Array(0);
|
|
1198
1432
|
var X25519_PUBLIC_KEY_LENGTH = 32;
|
|
1199
1433
|
var X25519_SECRET_KEY_LENGTH = 32;
|
|
1200
1434
|
var CEK_LENGTH = 32;
|
|
1201
|
-
var
|
|
1435
|
+
var NONCE_LENGTH2 = 24;
|
|
1202
1436
|
var WRAP_LENGTH = 48;
|
|
1203
1437
|
var SLOTS_MAC_LENGTH = 32;
|
|
1204
1438
|
if (CARDANO_POE_HKDF_INFO_KEK.length !== 18) {
|
|
@@ -1209,18 +1443,9 @@ if (CARDANO_POE_HKDF_INFO_KEK_MLKEM768X25519.length !== 33) {
|
|
|
1209
1443
|
"CARDANO_POE_HKDF_INFO_KEK_MLKEM768X25519 byte-length invariant violated (expected 33)"
|
|
1210
1444
|
);
|
|
1211
1445
|
}
|
|
1212
|
-
if (CARDANO_POE_HKDF_INFO_SLOTS_MAC.length !== 24) {
|
|
1213
|
-
throw new Error("CARDANO_POE_HKDF_INFO_SLOTS_MAC byte-length invariant violated (expected 24)");
|
|
1214
|
-
}
|
|
1215
1446
|
if (ZERO_NONCE_12.length !== 12) {
|
|
1216
1447
|
throw new Error("ZERO_NONCE_12 byte-length invariant violated (expected 12)");
|
|
1217
1448
|
}
|
|
1218
|
-
function concat(a, b) {
|
|
1219
|
-
const out = new Uint8Array(a.length + b.length);
|
|
1220
|
-
out.set(a, 0);
|
|
1221
|
-
out.set(b, a.length);
|
|
1222
|
-
return out;
|
|
1223
|
-
}
|
|
1224
1449
|
function uniformIndexBelow(m) {
|
|
1225
1450
|
const limit = 4294967296 - 4294967296 % m;
|
|
1226
1451
|
const buf = new Uint32Array(1);
|
|
@@ -1251,7 +1476,7 @@ function wrapSlotX25519(args) {
|
|
|
1251
1476
|
const shared = x25519Ecdh({ secretKey: privEph, theirPublicKey: args.pubR });
|
|
1252
1477
|
const kek = hkdfSha256({
|
|
1253
1478
|
ikm: shared,
|
|
1254
|
-
salt:
|
|
1479
|
+
salt: x25519KekSalt({ nonce: args.nonce, epk, pubR: args.pubR }),
|
|
1255
1480
|
info: CARDANO_POE_HKDF_INFO_KEK,
|
|
1256
1481
|
length: 32
|
|
1257
1482
|
});
|
|
@@ -1276,7 +1501,7 @@ function wrapSlotMlkem768X25519(args) {
|
|
|
1276
1501
|
}
|
|
1277
1502
|
const kek = hkdfSha256({
|
|
1278
1503
|
ikm: ss,
|
|
1279
|
-
salt: xwingKekSalt({ kemCt: enc, pubR: args.pubR }),
|
|
1504
|
+
salt: xwingKekSalt({ nonce: args.nonce, kemCt: enc, pubR: args.pubR }),
|
|
1280
1505
|
info: CARDANO_POE_HKDF_INFO_KEK_MLKEM768X25519,
|
|
1281
1506
|
length: 32
|
|
1282
1507
|
});
|
|
@@ -1289,13 +1514,13 @@ function wrapSlotMlkem768X25519(args) {
|
|
|
1289
1514
|
if (wrap.length !== WRAP_LENGTH) {
|
|
1290
1515
|
throw new Error(`internal: wrap.length=${wrap.length}, expected ${WRAP_LENGTH}`);
|
|
1291
1516
|
}
|
|
1292
|
-
return { kem_ct:
|
|
1517
|
+
return { kem_ct: enc, wrap };
|
|
1293
1518
|
}
|
|
1294
1519
|
function eciesSealedPoeWrap(args) {
|
|
1295
1520
|
const { plaintext, recipientPublicKeys } = args;
|
|
1296
1521
|
const kem = args.kem ?? "x25519";
|
|
1297
1522
|
const n = recipientPublicKeys.length;
|
|
1298
|
-
|
|
1523
|
+
const hashesHash = itemHashesHash(args.hashes);
|
|
1299
1524
|
if (n < 1) {
|
|
1300
1525
|
throw new EciesSealedPoeError(
|
|
1301
1526
|
"ENC_SLOTS_EMPTY",
|
|
@@ -1351,21 +1576,20 @@ function eciesSealedPoeWrap(args) {
|
|
|
1351
1576
|
}
|
|
1352
1577
|
}
|
|
1353
1578
|
const cek = args.cek ?? utils_js.randomBytes(CEK_LENGTH);
|
|
1354
|
-
const nonce = args.nonce ?? utils_js.randomBytes(
|
|
1579
|
+
const nonce = args.nonce ?? utils_js.randomBytes(NONCE_LENGTH2);
|
|
1355
1580
|
if (cek.length !== CEK_LENGTH) {
|
|
1356
1581
|
throw new EciesSealedPoeError(
|
|
1357
1582
|
"INVALID_CEK_LENGTH",
|
|
1358
1583
|
`cek MUST be exactly ${CEK_LENGTH} bytes, got ${cek.length}`
|
|
1359
1584
|
);
|
|
1360
1585
|
}
|
|
1361
|
-
if (nonce.length !==
|
|
1586
|
+
if (nonce.length !== NONCE_LENGTH2) {
|
|
1362
1587
|
throw new EciesSealedPoeError(
|
|
1363
1588
|
"NONCE_LENGTH_MISMATCH",
|
|
1364
|
-
`nonce MUST be exactly ${
|
|
1589
|
+
`nonce MUST be exactly ${NONCE_LENGTH2} bytes, got ${nonce.length}`
|
|
1365
1590
|
);
|
|
1366
1591
|
}
|
|
1367
1592
|
let envelope;
|
|
1368
|
-
let slotsHash;
|
|
1369
1593
|
if (kem === "x25519") {
|
|
1370
1594
|
const slots = [];
|
|
1371
1595
|
for (let i = 0; i < n; i++) {
|
|
@@ -1374,6 +1598,7 @@ function eciesSealedPoeWrap(args) {
|
|
|
1374
1598
|
pubR: recipientPublicKeys[i],
|
|
1375
1599
|
privEph: args.ephemeralSecrets ? args.ephemeralSecrets[i] : void 0,
|
|
1376
1600
|
cek,
|
|
1601
|
+
nonce,
|
|
1377
1602
|
slotIdx: i
|
|
1378
1603
|
})
|
|
1379
1604
|
);
|
|
@@ -1381,14 +1606,20 @@ function eciesSealedPoeWrap(args) {
|
|
|
1381
1606
|
if (args.skipShuffle !== true) {
|
|
1382
1607
|
csprngShuffle(slots);
|
|
1383
1608
|
}
|
|
1384
|
-
slotsHash = computeSlotsHash({
|
|
1609
|
+
const slotsHash = computeSlotsHash({
|
|
1610
|
+
aead: SEALED_POE_AEAD,
|
|
1611
|
+
kem: "x25519",
|
|
1612
|
+
nonce,
|
|
1613
|
+
slots,
|
|
1614
|
+
hashesHash
|
|
1615
|
+
});
|
|
1385
1616
|
envelope = {
|
|
1386
1617
|
scheme: 1,
|
|
1387
|
-
aead:
|
|
1618
|
+
aead: SEALED_POE_AEAD,
|
|
1388
1619
|
kem: "x25519",
|
|
1389
1620
|
nonce,
|
|
1390
1621
|
slots,
|
|
1391
|
-
slots_mac:
|
|
1622
|
+
slots_mac: sizedSlotsMac(cek, slotsHash)
|
|
1392
1623
|
};
|
|
1393
1624
|
} else {
|
|
1394
1625
|
const slots = [];
|
|
@@ -1397,83 +1628,55 @@ function eciesSealedPoeWrap(args) {
|
|
|
1397
1628
|
wrapSlotMlkem768X25519({
|
|
1398
1629
|
pubR: recipientPublicKeys[i],
|
|
1399
1630
|
eseed: args.eseeds ? args.eseeds[i] : void 0,
|
|
1400
|
-
cek
|
|
1631
|
+
cek,
|
|
1632
|
+
nonce
|
|
1401
1633
|
})
|
|
1402
1634
|
);
|
|
1403
1635
|
}
|
|
1404
1636
|
if (args.skipShuffle !== true) {
|
|
1405
1637
|
csprngShuffle(slots);
|
|
1406
1638
|
}
|
|
1407
|
-
slotsHash = computeSlotsHash({
|
|
1639
|
+
const slotsHash = computeSlotsHash({
|
|
1640
|
+
aead: SEALED_POE_AEAD,
|
|
1641
|
+
kem: "mlkem768x25519",
|
|
1642
|
+
nonce,
|
|
1643
|
+
slots,
|
|
1644
|
+
hashesHash
|
|
1645
|
+
});
|
|
1408
1646
|
envelope = {
|
|
1409
1647
|
scheme: 1,
|
|
1410
|
-
aead:
|
|
1648
|
+
aead: SEALED_POE_AEAD,
|
|
1411
1649
|
kem: "mlkem768x25519",
|
|
1412
1650
|
nonce,
|
|
1413
1651
|
slots,
|
|
1414
|
-
slots_mac:
|
|
1652
|
+
slots_mac: sizedSlotsMac(cek, slotsHash)
|
|
1415
1653
|
};
|
|
1416
1654
|
}
|
|
1417
|
-
const
|
|
1418
|
-
|
|
1419
|
-
kem: envelope.kem,
|
|
1420
|
-
nonce,
|
|
1421
|
-
slotsHash,
|
|
1422
|
-
slotsMac: envelope.slots_mac
|
|
1423
|
-
});
|
|
1424
|
-
const ciphertext = xchacha20Poly1305Encrypt({
|
|
1425
|
-
key: payloadKey,
|
|
1426
|
-
nonce,
|
|
1427
|
-
aad: adContent,
|
|
1655
|
+
const ciphertext = streamSeal({
|
|
1656
|
+
payloadKey: slotsPayloadKey({ cek, nonce }),
|
|
1428
1657
|
plaintext
|
|
1429
1658
|
});
|
|
1430
1659
|
return { envelope, ciphertext };
|
|
1431
1660
|
}
|
|
1432
|
-
function
|
|
1433
|
-
const
|
|
1434
|
-
ikm: cek,
|
|
1435
|
-
salt: EMPTY_SALT,
|
|
1436
|
-
info: CARDANO_POE_HKDF_INFO_SLOTS_MAC,
|
|
1437
|
-
length: 32
|
|
1438
|
-
});
|
|
1439
|
-
const slotsMac = hmac_js.hmac(sha2_js.sha256, hmacKey, slotsHash);
|
|
1661
|
+
function sizedSlotsMac(cek, slotsHash) {
|
|
1662
|
+
const slotsMac = computeSlotsMac({ cek, slotsHash });
|
|
1440
1663
|
if (slotsMac.length !== SLOTS_MAC_LENGTH) {
|
|
1441
1664
|
throw new Error(`internal: slots_mac.length=${slotsMac.length}, expected ${SLOTS_MAC_LENGTH}`);
|
|
1442
1665
|
}
|
|
1443
1666
|
return slotsMac;
|
|
1444
1667
|
}
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
var CHUNK_MAX_BYTES2 = 64;
|
|
1448
|
-
var UTF8_ENCODER2 = new TextEncoder();
|
|
1449
|
-
function chunkBytes(value) {
|
|
1450
|
-
if (value.length === 0) return [new Uint8Array(0)];
|
|
1451
|
-
const chunks = [];
|
|
1452
|
-
for (let i = 0; i < value.length; i += CHUNK_MAX_BYTES2) {
|
|
1453
|
-
chunks.push(value.subarray(i, Math.min(i + CHUNK_MAX_BYTES2, value.length)));
|
|
1454
|
-
}
|
|
1455
|
-
return chunks;
|
|
1456
|
-
}
|
|
1457
|
-
function chunkUri(uri) {
|
|
1458
|
-
const bytes = UTF8_ENCODER2.encode(uri);
|
|
1459
|
-
if (bytes.length === 0) return [""];
|
|
1460
|
-
if (bytes.length <= CHUNK_MAX_BYTES2) return [uri];
|
|
1461
|
-
const decoder = new TextDecoder("utf-8", { fatal: true });
|
|
1462
|
-
const chunks = [];
|
|
1463
|
-
let cursor = 0;
|
|
1464
|
-
while (cursor < bytes.length) {
|
|
1465
|
-
let end = Math.min(cursor + CHUNK_MAX_BYTES2, bytes.length);
|
|
1466
|
-
while (end < bytes.length && (bytes[end] & 192) === 128) end--;
|
|
1467
|
-
chunks.push(decoder.decode(bytes.subarray(cursor, end)));
|
|
1468
|
-
cursor = end;
|
|
1469
|
-
}
|
|
1470
|
-
return chunks;
|
|
1471
|
-
}
|
|
1668
|
+
new TextEncoder();
|
|
1669
|
+
new TextEncoder();
|
|
1472
1670
|
|
|
1473
1671
|
// src/client/off-host-sign.ts
|
|
1474
1672
|
var EMPTY_BYTES2 = new Uint8Array(0);
|
|
1475
1673
|
var ED25519_PUBLIC_KEY_LENGTH = 32;
|
|
1476
1674
|
var ED25519_SIGNATURE_LENGTH = 64;
|
|
1675
|
+
function cloneToOwnedBuffer(src) {
|
|
1676
|
+
const out = new Uint8Array(new ArrayBuffer(src.length));
|
|
1677
|
+
out.set(src);
|
|
1678
|
+
return out;
|
|
1679
|
+
}
|
|
1477
1680
|
var OffHostSignError = class extends Error {
|
|
1478
1681
|
code;
|
|
1479
1682
|
constructor(code, message) {
|
|
@@ -1532,8 +1735,7 @@ function assembleCoseSign1(args) {
|
|
|
1532
1735
|
payload: null,
|
|
1533
1736
|
signature: args.signature
|
|
1534
1737
|
});
|
|
1535
|
-
const
|
|
1536
|
-
const sigEntry = { cose_sign1: chunks };
|
|
1738
|
+
const sigEntry = { cose_sign1: cloneToOwnedBuffer(coseSign1Bytes) };
|
|
1537
1739
|
return { coseSign1Bytes, sigEntry };
|
|
1538
1740
|
}
|
|
1539
1741
|
function prepareSigStructureHashed(args) {
|
|
@@ -1575,8 +1777,7 @@ function assembleCoseSign1Hashed(args) {
|
|
|
1575
1777
|
payload: null,
|
|
1576
1778
|
signature: args.signature
|
|
1577
1779
|
});
|
|
1578
|
-
const
|
|
1579
|
-
const sigEntry = { cose_sign1: chunks };
|
|
1780
|
+
const sigEntry = { cose_sign1: cloneToOwnedBuffer(coseSign1Bytes) };
|
|
1580
1781
|
return { coseSign1Bytes, sigEntry };
|
|
1581
1782
|
}
|
|
1582
1783
|
|
|
@@ -1903,7 +2104,16 @@ function parseHttpError(args) {
|
|
|
1903
2104
|
return new ForbiddenError(init);
|
|
1904
2105
|
case "insufficient-scope":
|
|
1905
2106
|
return new InsufficientScopeError(init);
|
|
2107
|
+
// The three 402 funding/affordability failures are one condition to a
|
|
2108
|
+
// caller: the account cannot fund the operation. `insufficient-funds` is the
|
|
2109
|
+
// balance shortfall; `insufficient-storage-credit` is the storage funding
|
|
2110
|
+
// source being out of credit; `no-funding-grant` is the absence of any
|
|
2111
|
+
// funding source entitling the account beyond the free window. All three
|
|
2112
|
+
// surface as the same funding error so a caller routes the user to top up
|
|
2113
|
+
// without branching on the code.
|
|
1906
2114
|
case "insufficient-funds":
|
|
2115
|
+
case "insufficient-storage-credit":
|
|
2116
|
+
case "no-funding-grant":
|
|
1907
2117
|
return new InsufficientFundsError(init);
|
|
1908
2118
|
case "quote-expired":
|
|
1909
2119
|
return new QuoteExpiredError(init);
|
|
@@ -2015,6 +2225,25 @@ var InvalidClientConfigError = class extends Error {
|
|
|
2015
2225
|
function bytesToHex(bytes) {
|
|
2016
2226
|
return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
2017
2227
|
}
|
|
2228
|
+
|
|
2229
|
+
// src/client/partial-upload-error.ts
|
|
2230
|
+
var PartialUploadError = class extends Error {
|
|
2231
|
+
response;
|
|
2232
|
+
failed;
|
|
2233
|
+
constructor(response) {
|
|
2234
|
+
const failed = response.uploads.filter((u) => u.ok === false);
|
|
2235
|
+
super(
|
|
2236
|
+
`${failed.length} of ${response.uploads.length} upload(s) failed: ${failed.map((f) => `[${f.idx}] ${f.error.code} \u2014 ${f.error.detail}`).join("; ")}`
|
|
2237
|
+
);
|
|
2238
|
+
this.name = "PartialUploadError";
|
|
2239
|
+
this.response = response;
|
|
2240
|
+
this.failed = failed;
|
|
2241
|
+
}
|
|
2242
|
+
/** Convenience: the `idx` of every failed entry, in input order. */
|
|
2243
|
+
get failedIndices() {
|
|
2244
|
+
return this.failed.map((f) => f.idx);
|
|
2245
|
+
}
|
|
2246
|
+
};
|
|
2018
2247
|
function encodeCanonicalCbor4(value) {
|
|
2019
2248
|
return cbor2.encode(value, {
|
|
2020
2249
|
cde: true,
|
|
@@ -2077,24 +2306,493 @@ function encodeLeavesList(args) {
|
|
|
2077
2306
|
return encodeCanonicalCbor4(map);
|
|
2078
2307
|
}
|
|
2079
2308
|
|
|
2080
|
-
// src/client/
|
|
2081
|
-
var
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2309
|
+
// src/client/resumable-source.ts
|
|
2310
|
+
var openHandlePromise;
|
|
2311
|
+
async function loadOpen() {
|
|
2312
|
+
if (openHandlePromise === void 0) {
|
|
2313
|
+
openHandlePromise = import('fs/promises').then((fs) => fs.open);
|
|
2314
|
+
}
|
|
2315
|
+
return openHandlePromise;
|
|
2316
|
+
}
|
|
2317
|
+
var HASH_STREAM_CHUNK_BYTES = 1024 * 1024;
|
|
2318
|
+
function isResumableSource(value) {
|
|
2319
|
+
return typeof value === "object" && value !== null && typeof value.size === "number" && typeof value.slice === "function" && typeof value.stream === "function" && // A Blob/File shares all three members but exposes `.arrayBuffer`; exclude it
|
|
2320
|
+
// here so the adapter contract is unambiguous regardless of check order. A
|
|
2321
|
+
// Blob is handled by its own branch, which adapts `.slice`/`.stream` to bytes.
|
|
2322
|
+
typeof value.arrayBuffer !== "function";
|
|
2323
|
+
}
|
|
2324
|
+
function isBlobLike(value) {
|
|
2325
|
+
return typeof value === "object" && value !== null && typeof value.size === "number" && typeof value.slice === "function" && typeof value.arrayBuffer === "function";
|
|
2326
|
+
}
|
|
2327
|
+
async function blobSlice(blob, start, end) {
|
|
2328
|
+
return new Uint8Array(await blob.slice(start, end).arrayBuffer());
|
|
2329
|
+
}
|
|
2330
|
+
async function* blobStream(blob) {
|
|
2331
|
+
const reader = blob.stream().getReader();
|
|
2332
|
+
try {
|
|
2333
|
+
for (; ; ) {
|
|
2334
|
+
const { done, value } = await reader.read();
|
|
2335
|
+
if (done) break;
|
|
2336
|
+
if (value) yield value;
|
|
2337
|
+
}
|
|
2338
|
+
} finally {
|
|
2339
|
+
reader.releaseLock();
|
|
2340
|
+
}
|
|
2341
|
+
}
|
|
2342
|
+
function fromBlob(blob) {
|
|
2343
|
+
return {
|
|
2344
|
+
size: blob.size,
|
|
2345
|
+
slice: (start, end) => blobSlice(blob, start, end),
|
|
2346
|
+
stream: () => blobStream(blob)
|
|
2347
|
+
};
|
|
2348
|
+
}
|
|
2349
|
+
function fromBytes(bytes) {
|
|
2350
|
+
return {
|
|
2351
|
+
size: bytes.byteLength,
|
|
2352
|
+
slice: (start, end) => bytes.subarray(start, end),
|
|
2353
|
+
stream: async function* () {
|
|
2354
|
+
for (let offset = 0; offset < bytes.byteLength; offset += HASH_STREAM_CHUNK_BYTES) {
|
|
2355
|
+
yield bytes.subarray(offset, Math.min(offset + HASH_STREAM_CHUNK_BYTES, bytes.byteLength));
|
|
2356
|
+
}
|
|
2357
|
+
}
|
|
2358
|
+
};
|
|
2359
|
+
}
|
|
2360
|
+
async function fromPath(path) {
|
|
2361
|
+
const open = await loadOpen();
|
|
2362
|
+
const sliceAt = async (start, end) => {
|
|
2363
|
+
const length = end - start;
|
|
2364
|
+
if (length <= 0) return new Uint8Array(0);
|
|
2365
|
+
const handle = await open(path, "r");
|
|
2366
|
+
try {
|
|
2367
|
+
const buffer = new Uint8Array(length);
|
|
2368
|
+
let filled = 0;
|
|
2369
|
+
while (filled < length) {
|
|
2370
|
+
const { bytesRead } = await handle.read(buffer, filled, length - filled, start + filled);
|
|
2371
|
+
if (bytesRead === 0) break;
|
|
2372
|
+
filled += bytesRead;
|
|
2373
|
+
}
|
|
2374
|
+
return filled === length ? buffer : buffer.subarray(0, filled);
|
|
2375
|
+
} finally {
|
|
2376
|
+
await handle.close();
|
|
2377
|
+
}
|
|
2378
|
+
};
|
|
2379
|
+
const streamFile = async function* () {
|
|
2380
|
+
const handle = await open(path, "r");
|
|
2381
|
+
try {
|
|
2382
|
+
const buffer = new Uint8Array(HASH_STREAM_CHUNK_BYTES);
|
|
2383
|
+
let position = 0;
|
|
2384
|
+
for (; ; ) {
|
|
2385
|
+
const { bytesRead } = await handle.read(buffer, 0, buffer.length, position);
|
|
2386
|
+
if (bytesRead === 0) break;
|
|
2387
|
+
position += bytesRead;
|
|
2388
|
+
yield buffer.slice(0, bytesRead);
|
|
2389
|
+
}
|
|
2390
|
+
} finally {
|
|
2391
|
+
await handle.close();
|
|
2392
|
+
}
|
|
2393
|
+
};
|
|
2394
|
+
const { size } = await statSize(path);
|
|
2395
|
+
return { size, slice: sliceAt, stream: streamFile };
|
|
2396
|
+
}
|
|
2397
|
+
async function statSize(path) {
|
|
2398
|
+
const open = await loadOpen();
|
|
2399
|
+
const handle = await open(path, "r");
|
|
2400
|
+
try {
|
|
2401
|
+
const stats = await handle.stat();
|
|
2402
|
+
return { size: stats.size };
|
|
2403
|
+
} finally {
|
|
2404
|
+
await handle.close();
|
|
2405
|
+
}
|
|
2406
|
+
}
|
|
2407
|
+
async function toResumableSource(input) {
|
|
2408
|
+
if (typeof input === "string") return fromPath(input);
|
|
2409
|
+
if (input instanceof Uint8Array) return fromBytes(input);
|
|
2410
|
+
if (isBlobLike(input)) return fromBlob(input);
|
|
2411
|
+
if (isResumableSource(input)) return input;
|
|
2412
|
+
throw new TypeError(
|
|
2413
|
+
"uploadResumable: unsupported source. Pass a Blob/File, a Uint8Array, a filesystem path string, or a ResumableSource."
|
|
2414
|
+
);
|
|
2415
|
+
}
|
|
2416
|
+
|
|
2417
|
+
// src/client/resumable-upload.ts
|
|
2418
|
+
var DEFAULT_RESUMABLE_THRESHOLD_BYTES = 50331648;
|
|
2419
|
+
var DEFAULT_RESUMABLE_CHUNK_BYTES = 50331648;
|
|
2420
|
+
var DEFAULT_PARALLELISM = 4;
|
|
2421
|
+
var DEFAULT_MAX_CHUNK_RETRIES = 4;
|
|
2422
|
+
var DEFAULT_CONTENT_TYPE = "application/octet-stream";
|
|
2423
|
+
var DEFAULT_TARGET = "arweave";
|
|
2424
|
+
var ATTEMPT_POLL_INTERVAL_MS = 1e3;
|
|
2425
|
+
var ATTEMPT_POLL_MAX_ATTEMPTS = 600;
|
|
2426
|
+
function bytesToHex2(bytes) {
|
|
2427
|
+
return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
2428
|
+
}
|
|
2429
|
+
var B64_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
2430
|
+
function bytesToBase64(bytes) {
|
|
2431
|
+
let out = "";
|
|
2432
|
+
let i = 0;
|
|
2433
|
+
for (; i + 2 < bytes.length; i += 3) {
|
|
2434
|
+
const n = bytes[i] << 16 | bytes[i + 1] << 8 | bytes[i + 2];
|
|
2435
|
+
out += B64_ALPHABET[n >> 18 & 63] + B64_ALPHABET[n >> 12 & 63] + B64_ALPHABET[n >> 6 & 63] + B64_ALPHABET[n & 63];
|
|
2436
|
+
}
|
|
2437
|
+
const rem = bytes.length - i;
|
|
2438
|
+
if (rem === 1) {
|
|
2439
|
+
const n = bytes[i] << 16;
|
|
2440
|
+
out += B64_ALPHABET[n >> 18 & 63] + B64_ALPHABET[n >> 12 & 63] + "==";
|
|
2441
|
+
} else if (rem === 2) {
|
|
2442
|
+
const n = bytes[i] << 16 | bytes[i + 1] << 8;
|
|
2443
|
+
out += B64_ALPHABET[n >> 18 & 63] + B64_ALPHABET[n >> 12 & 63] + B64_ALPHABET[n >> 6 & 63] + "=";
|
|
2444
|
+
}
|
|
2445
|
+
return out;
|
|
2446
|
+
}
|
|
2447
|
+
function jsonHeaders(config, idempotencyKey) {
|
|
2448
|
+
const headers = new Headers({ "content-type": "application/json", accept: "application/json" });
|
|
2449
|
+
if (config.apiKey !== void 0) headers.set("authorization", `Bearer ${config.apiKey}`);
|
|
2450
|
+
if (idempotencyKey !== void 0) headers.set("idempotency-key", idempotencyKey);
|
|
2451
|
+
return headers;
|
|
2452
|
+
}
|
|
2453
|
+
function octetHeaders(config, length, digestBase64) {
|
|
2454
|
+
const headers = new Headers({
|
|
2455
|
+
"content-type": "application/octet-stream",
|
|
2456
|
+
accept: "application/json",
|
|
2457
|
+
"content-length": String(length),
|
|
2458
|
+
digest: `sha-256=${digestBase64}`
|
|
2459
|
+
});
|
|
2460
|
+
if (config.apiKey !== void 0) headers.set("authorization", `Bearer ${config.apiKey}`);
|
|
2461
|
+
return headers;
|
|
2462
|
+
}
|
|
2463
|
+
var SESSIONS_PATH = "/api/v1/poe/uploads/sessions";
|
|
2464
|
+
function chunkRange(index, chunkBytes, totalBytes) {
|
|
2465
|
+
const start = index * chunkBytes;
|
|
2466
|
+
return [start, Math.min(start + chunkBytes, totalBytes)];
|
|
2467
|
+
}
|
|
2468
|
+
function missingIndices(received, chunkCount) {
|
|
2469
|
+
const have = new Set(received);
|
|
2470
|
+
const out = [];
|
|
2471
|
+
for (let i = 0; i < chunkCount; i++) if (!have.has(i)) out.push(i);
|
|
2472
|
+
return out;
|
|
2473
|
+
}
|
|
2474
|
+
function serverMissing(status) {
|
|
2475
|
+
if (Array.isArray(status.missing)) return status.missing;
|
|
2476
|
+
return missingIndices(status.received, status.chunk_count);
|
|
2477
|
+
}
|
|
2478
|
+
async function hashWholeFile(source) {
|
|
2479
|
+
return bytesToHex2(await sha256Stream(source.stream()));
|
|
2480
|
+
}
|
|
2481
|
+
async function createSession(config, body, signal) {
|
|
2482
|
+
const response = await config.fetch(`${config.baseUrl}${SESSIONS_PATH}`, {
|
|
2483
|
+
method: "POST",
|
|
2484
|
+
headers: jsonHeaders(config),
|
|
2485
|
+
body: JSON.stringify(body),
|
|
2486
|
+
...signal ? { signal } : {}
|
|
2487
|
+
});
|
|
2488
|
+
await throwIfNotOk(response);
|
|
2489
|
+
return await readJson(response);
|
|
2490
|
+
}
|
|
2491
|
+
async function getSessionStatus(config, sessionId, signal) {
|
|
2492
|
+
const response = await config.fetch(
|
|
2493
|
+
`${config.baseUrl}${SESSIONS_PATH}/${encodeURIComponent(sessionId)}`,
|
|
2494
|
+
{
|
|
2495
|
+
method: "GET",
|
|
2496
|
+
headers: jsonHeaders(config),
|
|
2497
|
+
...signal ? { signal } : {}
|
|
2498
|
+
}
|
|
2499
|
+
);
|
|
2500
|
+
await throwIfNotOk(response);
|
|
2501
|
+
return await readJson(response);
|
|
2502
|
+
}
|
|
2503
|
+
async function putChunk(config, sessionId, index, bytes, signal) {
|
|
2504
|
+
const digest = bytesToBase64(sha256(bytes));
|
|
2505
|
+
const response = await config.fetch(
|
|
2506
|
+
`${config.baseUrl}${SESSIONS_PATH}/${encodeURIComponent(sessionId)}/chunks/${index}`,
|
|
2507
|
+
{
|
|
2508
|
+
method: "PUT",
|
|
2509
|
+
headers: octetHeaders(config, bytes.byteLength, digest),
|
|
2510
|
+
// A Blob body streams without copying; a matching-digest re-PUT is an
|
|
2511
|
+
// idempotent 200 server-side, so a retried chunk is always safe.
|
|
2512
|
+
body: new Blob([bytes], { type: "application/octet-stream" }),
|
|
2513
|
+
...signal ? { signal } : {}
|
|
2514
|
+
}
|
|
2515
|
+
);
|
|
2516
|
+
await throwIfNotOk(response);
|
|
2517
|
+
return await readJson(response);
|
|
2518
|
+
}
|
|
2519
|
+
async function completeSession(config, sessionId, idempotencyKey, signal) {
|
|
2520
|
+
const response = await config.fetch(
|
|
2521
|
+
`${config.baseUrl}${SESSIONS_PATH}/${encodeURIComponent(sessionId)}/complete`,
|
|
2522
|
+
{
|
|
2523
|
+
method: "POST",
|
|
2524
|
+
headers: jsonHeaders(config, idempotencyKey),
|
|
2525
|
+
...signal ? { signal } : {}
|
|
2526
|
+
}
|
|
2527
|
+
);
|
|
2528
|
+
await throwIfNotOk(response);
|
|
2529
|
+
return await readJson(response);
|
|
2530
|
+
}
|
|
2531
|
+
async function pollAttempt(config, attemptId, signal) {
|
|
2532
|
+
for (let attempt = 0; attempt < ATTEMPT_POLL_MAX_ATTEMPTS; attempt++) {
|
|
2533
|
+
const response = await config.fetch(
|
|
2534
|
+
`${config.baseUrl}/api/v1/poe/uploads/attempts/${encodeURIComponent(attemptId)}`,
|
|
2535
|
+
{
|
|
2536
|
+
method: "GET",
|
|
2537
|
+
headers: jsonHeaders(config),
|
|
2538
|
+
...signal ? { signal } : {}
|
|
2539
|
+
}
|
|
2088
2540
|
);
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2541
|
+
await throwIfNotOk(response);
|
|
2542
|
+
const status = await readJson(response);
|
|
2543
|
+
if (status.state !== "reserved") return status;
|
|
2544
|
+
await delay(ATTEMPT_POLL_INTERVAL_MS, signal);
|
|
2092
2545
|
}
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2546
|
+
throw new ResumableUploadError(
|
|
2547
|
+
"ATTEMPT_POLL_TIMEOUT",
|
|
2548
|
+
`upload attempt ${attemptId} did not reach a terminal state in time`
|
|
2549
|
+
);
|
|
2550
|
+
}
|
|
2551
|
+
function delay(ms, signal) {
|
|
2552
|
+
return new Promise((resolve, reject) => {
|
|
2553
|
+
if (signal?.aborted) {
|
|
2554
|
+
reject(signalReason(signal));
|
|
2555
|
+
return;
|
|
2556
|
+
}
|
|
2557
|
+
const timer = setTimeout(() => {
|
|
2558
|
+
signal?.removeEventListener("abort", onAbort);
|
|
2559
|
+
resolve();
|
|
2560
|
+
}, ms);
|
|
2561
|
+
const onAbort = () => {
|
|
2562
|
+
clearTimeout(timer);
|
|
2563
|
+
reject(signalReason(signal));
|
|
2564
|
+
};
|
|
2565
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
2566
|
+
});
|
|
2567
|
+
}
|
|
2568
|
+
function signalReason(signal) {
|
|
2569
|
+
const reason = signal?.reason;
|
|
2570
|
+
return reason instanceof Error ? reason : new ResumableUploadError("ABORTED", "upload aborted");
|
|
2571
|
+
}
|
|
2572
|
+
var ResumableUploadError = class extends Error {
|
|
2573
|
+
code;
|
|
2574
|
+
constructor(code, message) {
|
|
2575
|
+
super(message);
|
|
2576
|
+
this.name = "ResumableUploadError";
|
|
2577
|
+
this.code = code;
|
|
2096
2578
|
}
|
|
2097
2579
|
};
|
|
2580
|
+
async function uploadChunks(config, sessionId, source, chunkBytes, totalBytes, missing, parallelism, maxRetries, signal) {
|
|
2581
|
+
let cursor = 0;
|
|
2582
|
+
const workers = [];
|
|
2583
|
+
const lanes = Math.max(1, Math.min(parallelism, missing.length || 1));
|
|
2584
|
+
for (let lane = 0; lane < lanes; lane++) {
|
|
2585
|
+
workers.push(
|
|
2586
|
+
(async () => {
|
|
2587
|
+
for (; ; ) {
|
|
2588
|
+
if (signal?.aborted) throw signalReason(signal);
|
|
2589
|
+
const next = cursor++;
|
|
2590
|
+
if (next >= missing.length) return;
|
|
2591
|
+
const index = missing[next];
|
|
2592
|
+
const [start, end] = chunkRange(index, chunkBytes, totalBytes);
|
|
2593
|
+
const bytes = await source.slice(start, end);
|
|
2594
|
+
await putChunkWithRetry(config, sessionId, index, bytes, maxRetries, signal);
|
|
2595
|
+
}
|
|
2596
|
+
})()
|
|
2597
|
+
);
|
|
2598
|
+
}
|
|
2599
|
+
await Promise.all(workers);
|
|
2600
|
+
}
|
|
2601
|
+
async function putChunkWithRetry(config, sessionId, index, bytes, maxRetries, signal) {
|
|
2602
|
+
let lastError;
|
|
2603
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
2604
|
+
if (signal?.aborted) throw signalReason(signal);
|
|
2605
|
+
try {
|
|
2606
|
+
await putChunk(config, sessionId, index, bytes, signal);
|
|
2607
|
+
return;
|
|
2608
|
+
} catch (err) {
|
|
2609
|
+
if (signal?.aborted) throw signalReason(signal);
|
|
2610
|
+
if (isTerminalChunkError(err)) throw err;
|
|
2611
|
+
lastError = err;
|
|
2612
|
+
if (attempt < maxRetries) {
|
|
2613
|
+
await delay(Math.min(250 * 2 ** attempt, 8e3), signal);
|
|
2614
|
+
}
|
|
2615
|
+
}
|
|
2616
|
+
}
|
|
2617
|
+
throw new ResumableUploadError(
|
|
2618
|
+
"CHUNK_UPLOAD_FAILED",
|
|
2619
|
+
`chunk ${index} failed after ${maxRetries + 1} attempt(s): ${lastError instanceof Error ? lastError.message : String(lastError)}`
|
|
2620
|
+
);
|
|
2621
|
+
}
|
|
2622
|
+
async function uploadResumable(config, singleShot, input) {
|
|
2623
|
+
const source = await toResumableSource(input.source);
|
|
2624
|
+
const target = input.target ?? DEFAULT_TARGET;
|
|
2625
|
+
const threshold = input.threshold ?? DEFAULT_RESUMABLE_THRESHOLD_BYTES;
|
|
2626
|
+
const totalBytes = source.size;
|
|
2627
|
+
if (totalBytes <= threshold && input.sessionId === void 0) {
|
|
2628
|
+
const bytes = await source.slice(0, totalBytes);
|
|
2629
|
+
const result = await singleShot({
|
|
2630
|
+
target,
|
|
2631
|
+
bytes,
|
|
2632
|
+
...input.idempotencyKey !== void 0 ? { idempotencyKey: input.idempotencyKey } : {},
|
|
2633
|
+
...input.signal ? { signal: input.signal } : {}
|
|
2634
|
+
});
|
|
2635
|
+
return {
|
|
2636
|
+
uri: result.uri,
|
|
2637
|
+
sha256: result.sha256,
|
|
2638
|
+
bytes: result.bytes,
|
|
2639
|
+
deduplicated: false,
|
|
2640
|
+
mode: "single-shot"
|
|
2641
|
+
};
|
|
2642
|
+
}
|
|
2643
|
+
return runSession(config, source, target, totalBytes, input);
|
|
2644
|
+
}
|
|
2645
|
+
async function runSession(config, source, target, totalBytes, input) {
|
|
2646
|
+
const signal = input.signal;
|
|
2647
|
+
let sessionId;
|
|
2648
|
+
let chunkBytes;
|
|
2649
|
+
let missing;
|
|
2650
|
+
let declaredSha256;
|
|
2651
|
+
let gridTotalBytes;
|
|
2652
|
+
if (input.sessionId !== void 0) {
|
|
2653
|
+
const status = await getSessionStatus(config, input.sessionId, signal);
|
|
2654
|
+
if (status.state === "completed" && status.uri !== null) {
|
|
2655
|
+
return {
|
|
2656
|
+
uri: status.uri,
|
|
2657
|
+
sha256: status.sha256,
|
|
2658
|
+
bytes: status.total_bytes,
|
|
2659
|
+
deduplicated: false,
|
|
2660
|
+
mode: "chunked"
|
|
2661
|
+
};
|
|
2662
|
+
}
|
|
2663
|
+
if (status.state === "failed" || status.state === "expired") {
|
|
2664
|
+
throw new ResumableUploadError(
|
|
2665
|
+
"SESSION_FAILED",
|
|
2666
|
+
`cannot resume session ${input.sessionId} in state '${status.state}'`
|
|
2667
|
+
);
|
|
2668
|
+
}
|
|
2669
|
+
sessionId = status.session_id;
|
|
2670
|
+
declaredSha256 = status.sha256;
|
|
2671
|
+
chunkBytes = status.chunk_bytes;
|
|
2672
|
+
gridTotalBytes = status.total_bytes;
|
|
2673
|
+
missing = serverMissing(status);
|
|
2674
|
+
} else {
|
|
2675
|
+
declaredSha256 = await hashWholeFile(source);
|
|
2676
|
+
const requestedChunkBytes = input.chunkBytes ?? DEFAULT_RESUMABLE_CHUNK_BYTES;
|
|
2677
|
+
const created = await createSession(
|
|
2678
|
+
config,
|
|
2679
|
+
{
|
|
2680
|
+
target,
|
|
2681
|
+
sha256: declaredSha256,
|
|
2682
|
+
total_bytes: totalBytes,
|
|
2683
|
+
chunk_bytes: requestedChunkBytes,
|
|
2684
|
+
content_type: input.contentType ?? DEFAULT_CONTENT_TYPE
|
|
2685
|
+
},
|
|
2686
|
+
signal
|
|
2687
|
+
);
|
|
2688
|
+
if ("deduplicated" in created) {
|
|
2689
|
+
return {
|
|
2690
|
+
uri: created.uri,
|
|
2691
|
+
sha256: created.sha256,
|
|
2692
|
+
bytes: created.bytes,
|
|
2693
|
+
deduplicated: true,
|
|
2694
|
+
mode: "chunked"
|
|
2695
|
+
};
|
|
2696
|
+
}
|
|
2697
|
+
sessionId = created.session_id;
|
|
2698
|
+
chunkBytes = created.chunk_bytes;
|
|
2699
|
+
gridTotalBytes = totalBytes;
|
|
2700
|
+
missing = missingIndices(created.received, created.chunk_count);
|
|
2701
|
+
}
|
|
2702
|
+
if (missing.length > 0) {
|
|
2703
|
+
await uploadChunks(
|
|
2704
|
+
config,
|
|
2705
|
+
sessionId,
|
|
2706
|
+
source,
|
|
2707
|
+
chunkBytes,
|
|
2708
|
+
gridTotalBytes,
|
|
2709
|
+
missing,
|
|
2710
|
+
input.parallelism ?? DEFAULT_PARALLELISM,
|
|
2711
|
+
input.maxChunkRetries ?? DEFAULT_MAX_CHUNK_RETRIES,
|
|
2712
|
+
signal
|
|
2713
|
+
);
|
|
2714
|
+
}
|
|
2715
|
+
return finishSession(config, sessionId, declaredSha256, input);
|
|
2716
|
+
}
|
|
2717
|
+
async function finishSession(config, sessionId, declaredSha256, input) {
|
|
2718
|
+
const signal = input.signal;
|
|
2719
|
+
const idempotencyKey = input.idempotencyKey ?? `resumable-${declaredSha256}`;
|
|
2720
|
+
const COMPLETE_RETRIES = 2;
|
|
2721
|
+
for (let attempt = 0; attempt <= COMPLETE_RETRIES; attempt++) {
|
|
2722
|
+
try {
|
|
2723
|
+
const completion = await completeSession(config, sessionId, idempotencyKey, signal);
|
|
2724
|
+
if ("ok" in completion) {
|
|
2725
|
+
return {
|
|
2726
|
+
uri: completion.uri,
|
|
2727
|
+
sha256: completion.sha256,
|
|
2728
|
+
bytes: completion.bytes,
|
|
2729
|
+
// The server sends the number 0 for a dedup-on-commit (the bytes were
|
|
2730
|
+
// already stored, so nothing was charged); compare numerically.
|
|
2731
|
+
deduplicated: completion.charged_usd_micros === 0,
|
|
2732
|
+
mode: "chunked"
|
|
2733
|
+
};
|
|
2734
|
+
}
|
|
2735
|
+
return resolveAccepted(config, completion.attempt_id, signal);
|
|
2736
|
+
} catch (err) {
|
|
2737
|
+
if (attempt < COMPLETE_RETRIES && isIncompleteUpload(err)) {
|
|
2738
|
+
const status = await getSessionStatus(config, sessionId, signal);
|
|
2739
|
+
const stillMissing = serverMissing(status);
|
|
2740
|
+
if (stillMissing.length === 0) continue;
|
|
2741
|
+
await uploadChunks(
|
|
2742
|
+
config,
|
|
2743
|
+
sessionId,
|
|
2744
|
+
await toResumableSource(input.source),
|
|
2745
|
+
status.chunk_bytes,
|
|
2746
|
+
// Re-bound the resend grid against the server's declared total too, so a
|
|
2747
|
+
// source that grew during the upload cannot over-read the final chunk.
|
|
2748
|
+
status.total_bytes,
|
|
2749
|
+
stillMissing,
|
|
2750
|
+
input.parallelism ?? DEFAULT_PARALLELISM,
|
|
2751
|
+
input.maxChunkRetries ?? DEFAULT_MAX_CHUNK_RETRIES,
|
|
2752
|
+
signal
|
|
2753
|
+
);
|
|
2754
|
+
continue;
|
|
2755
|
+
}
|
|
2756
|
+
throw err;
|
|
2757
|
+
}
|
|
2758
|
+
}
|
|
2759
|
+
throw new ResumableUploadError(
|
|
2760
|
+
"SESSION_FAILED",
|
|
2761
|
+
`session ${sessionId} could not be completed after resending missing chunks`
|
|
2762
|
+
);
|
|
2763
|
+
}
|
|
2764
|
+
async function resolveAccepted(config, attemptId, signal) {
|
|
2765
|
+
const status = await pollAttempt(config, attemptId, signal);
|
|
2766
|
+
if (status.state === "released") {
|
|
2767
|
+
throw new ResumableUploadError(
|
|
2768
|
+
"ATTEMPT_FAILED",
|
|
2769
|
+
`upload attempt ${attemptId} was released: ${status.reason}`
|
|
2770
|
+
);
|
|
2771
|
+
}
|
|
2772
|
+
if (status.uri.length === 0) {
|
|
2773
|
+
throw new ResumableUploadError(
|
|
2774
|
+
"ATTEMPT_FAILED",
|
|
2775
|
+
`upload attempt ${attemptId} committed without a uri`
|
|
2776
|
+
);
|
|
2777
|
+
}
|
|
2778
|
+
return {
|
|
2779
|
+
uri: status.uri,
|
|
2780
|
+
sha256: status.sha256,
|
|
2781
|
+
bytes: status.bytes,
|
|
2782
|
+
// A committed attempt that charged nothing deduped against bytes already
|
|
2783
|
+
// stored for this account on this backend.
|
|
2784
|
+
deduplicated: status.charged_usd_micros === 0,
|
|
2785
|
+
mode: "chunked"
|
|
2786
|
+
};
|
|
2787
|
+
}
|
|
2788
|
+
function isTerminalChunkError(err) {
|
|
2789
|
+
if (!(err instanceof Label309HttpError)) return false;
|
|
2790
|
+
const status = err.httpStatus;
|
|
2791
|
+
return status >= 400 && status < 500 && status !== 408 && status !== 429;
|
|
2792
|
+
}
|
|
2793
|
+
function isIncompleteUpload(err) {
|
|
2794
|
+
return typeof err === "object" && err !== null && "code" in err && err.code === "incomplete-upload";
|
|
2795
|
+
}
|
|
2098
2796
|
|
|
2099
2797
|
// src/client/publish.ts
|
|
2100
2798
|
var ED25519_PUBLIC_KEY_LENGTH2 = 32;
|
|
@@ -2111,7 +2809,7 @@ var PublishError = class extends Error {
|
|
|
2111
2809
|
this.code = code;
|
|
2112
2810
|
}
|
|
2113
2811
|
};
|
|
2114
|
-
function
|
|
2812
|
+
function bytesToHex3(bytes) {
|
|
2115
2813
|
return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
2116
2814
|
}
|
|
2117
2815
|
function hexToBytes(hex) {
|
|
@@ -2132,14 +2830,14 @@ function toBytes(content) {
|
|
|
2132
2830
|
if (typeof content === "string") return new TextEncoder().encode(content);
|
|
2133
2831
|
return content;
|
|
2134
2832
|
}
|
|
2135
|
-
function
|
|
2833
|
+
function cloneToOwnedBuffer2(src) {
|
|
2136
2834
|
const out = new Uint8Array(new ArrayBuffer(src.length));
|
|
2137
2835
|
out.set(src);
|
|
2138
2836
|
return out;
|
|
2139
2837
|
}
|
|
2140
2838
|
function hashContent(bytes, alg) {
|
|
2141
|
-
if (alg === "sha2-256") return
|
|
2142
|
-
if (alg === "blake2b-256") return
|
|
2839
|
+
if (alg === "sha2-256") return cloneToOwnedBuffer2(sha256(bytes));
|
|
2840
|
+
if (alg === "blake2b-256") return cloneToOwnedBuffer2(blake2b256(bytes));
|
|
2143
2841
|
throw new PublishError(
|
|
2144
2842
|
"UNSUPPORTED_HASH_ALG",
|
|
2145
2843
|
`hashAlg must be 'sha2-256' or 'blake2b-256', got '${alg}'`
|
|
@@ -2223,29 +2921,45 @@ async function postPublish(config, recordBytesHex, quoteId, idempotencyKey) {
|
|
|
2223
2921
|
const parsed = await readJson2(response);
|
|
2224
2922
|
return { ...parsed, dedup_hit: response.status === 200 };
|
|
2225
2923
|
}
|
|
2226
|
-
|
|
2924
|
+
var singleShotUpload = (config) => async ({ target, bytes, idempotencyKey, signal }) => {
|
|
2227
2925
|
const form = new FormData();
|
|
2228
|
-
form.append("target",
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
`file_${idx}.bin`
|
|
2235
|
-
);
|
|
2236
|
-
}
|
|
2926
|
+
form.append("target", target);
|
|
2927
|
+
form.append(
|
|
2928
|
+
"file_0",
|
|
2929
|
+
new Blob([bytes], { type: "application/octet-stream" }),
|
|
2930
|
+
"file_0.bin"
|
|
2931
|
+
);
|
|
2237
2932
|
const response = await config.fetch(`${config.baseUrl}/api/v1/poe/uploads`, {
|
|
2238
2933
|
method: "POST",
|
|
2239
2934
|
headers: buildMultipartHeaders(config.apiKey, idempotencyKey),
|
|
2240
|
-
body: form
|
|
2935
|
+
body: form,
|
|
2936
|
+
...signal ? { signal } : {}
|
|
2241
2937
|
});
|
|
2242
2938
|
await throwIfNotOk2(response);
|
|
2243
2939
|
const result = await readJson2(response);
|
|
2244
|
-
const
|
|
2245
|
-
if (
|
|
2940
|
+
const entry = result.uploads[0];
|
|
2941
|
+
if (entry === void 0 || entry.ok === false) {
|
|
2246
2942
|
throw new PartialUploadError(result);
|
|
2247
2943
|
}
|
|
2248
|
-
|
|
2944
|
+
const ok = entry;
|
|
2945
|
+
return { uri: ok.uri, sha256: ok.sha256, bytes: ok.bytes };
|
|
2946
|
+
};
|
|
2947
|
+
async function uploadBlob(config, bytes, idempotencyKey) {
|
|
2948
|
+
const target = STORAGE_TARGET_ARWEAVE;
|
|
2949
|
+
if (bytes.byteLength <= DEFAULT_RESUMABLE_THRESHOLD_BYTES) {
|
|
2950
|
+
const single = await singleShotUpload(config)({
|
|
2951
|
+
target,
|
|
2952
|
+
bytes,
|
|
2953
|
+
...idempotencyKey !== void 0 ? { idempotencyKey } : {}
|
|
2954
|
+
});
|
|
2955
|
+
return single.uri;
|
|
2956
|
+
}
|
|
2957
|
+
const result = await uploadResumable(config, singleShotUpload(config), {
|
|
2958
|
+
target,
|
|
2959
|
+
source: bytes,
|
|
2960
|
+
...idempotencyKey !== void 0 ? { idempotencyKey } : {}
|
|
2961
|
+
});
|
|
2962
|
+
return result.uri;
|
|
2249
2963
|
}
|
|
2250
2964
|
async function publishContent(config, input) {
|
|
2251
2965
|
if (input.signer !== void 0) assertSigner(input.signer);
|
|
@@ -2257,7 +2971,7 @@ async function publishContent(config, input) {
|
|
|
2257
2971
|
items: [{ hashes: { [hashAlg]: digest } }]
|
|
2258
2972
|
};
|
|
2259
2973
|
const recordBytes = await encodeRecord(record, input.signer);
|
|
2260
|
-
return postPublish(config,
|
|
2974
|
+
return postPublish(config, bytesToHex3(recordBytes), input.quoteId, input.idempotencyKey);
|
|
2261
2975
|
}
|
|
2262
2976
|
var DIGEST_BYTE_LENGTH = {
|
|
2263
2977
|
"sha2-256": 32,
|
|
@@ -2289,14 +3003,14 @@ async function publishPrehashed(config, input) {
|
|
|
2289
3003
|
`hashes[${alg}] must be a ${expected}-byte digest (got ${bytes.length} bytes)`
|
|
2290
3004
|
);
|
|
2291
3005
|
}
|
|
2292
|
-
decoded[alg] =
|
|
3006
|
+
decoded[alg] = cloneToOwnedBuffer2(bytes);
|
|
2293
3007
|
}
|
|
2294
3008
|
const record = {
|
|
2295
3009
|
v: 1,
|
|
2296
3010
|
items: [{ hashes: decoded }]
|
|
2297
3011
|
};
|
|
2298
3012
|
const recordBytes = await encodeRecord(record, input.signer);
|
|
2299
|
-
return postPublish(config,
|
|
3013
|
+
return postPublish(config, bytesToHex3(recordBytes), input.quoteId, input.idempotencyKey);
|
|
2300
3014
|
}
|
|
2301
3015
|
async function publishSealed(config, input) {
|
|
2302
3016
|
if (input.signer !== void 0) assertSigner(input.signer);
|
|
@@ -2320,41 +3034,42 @@ async function publishSealed(config, input) {
|
|
|
2320
3034
|
const hashAlg = input.hashAlg ?? "sha2-256";
|
|
2321
3035
|
const plaintext = toBytes(input.content);
|
|
2322
3036
|
const plaintextDigest = hashContent(plaintext, hashAlg);
|
|
3037
|
+
const hashes2 = { [hashAlg]: plaintextDigest };
|
|
2323
3038
|
const sealed = eciesSealedPoeWrap({
|
|
2324
3039
|
plaintext,
|
|
3040
|
+
hashes: hashes2,
|
|
2325
3041
|
recipientPublicKeys: input.recipients.map((r) => r),
|
|
2326
3042
|
kem
|
|
2327
3043
|
});
|
|
2328
|
-
const
|
|
2329
|
-
const uri = uploadsResp.uploads[0].uri;
|
|
3044
|
+
const uri = await uploadBlob(config, sealed.ciphertext, input.idempotencyKey);
|
|
2330
3045
|
const env = sealed.envelope;
|
|
2331
3046
|
const slots = env.kem === "mlkem768x25519" ? env.slots.map((s) => ({
|
|
2332
|
-
kem_ct: s.kem_ct
|
|
2333
|
-
wrap:
|
|
3047
|
+
kem_ct: cloneToOwnedBuffer2(s.kem_ct),
|
|
3048
|
+
wrap: cloneToOwnedBuffer2(s.wrap)
|
|
2334
3049
|
})) : env.slots.map((s) => ({
|
|
2335
|
-
epk:
|
|
2336
|
-
wrap:
|
|
3050
|
+
epk: cloneToOwnedBuffer2(s.epk),
|
|
3051
|
+
wrap: cloneToOwnedBuffer2(s.wrap)
|
|
2337
3052
|
}));
|
|
2338
3053
|
const envelope = {
|
|
2339
3054
|
scheme: 1,
|
|
2340
3055
|
aead: env.aead,
|
|
2341
3056
|
kem: env.kem,
|
|
2342
|
-
nonce:
|
|
3057
|
+
nonce: cloneToOwnedBuffer2(env.nonce),
|
|
2343
3058
|
slots,
|
|
2344
|
-
slots_mac:
|
|
3059
|
+
slots_mac: cloneToOwnedBuffer2(env.slots_mac)
|
|
2345
3060
|
};
|
|
2346
3061
|
const record = {
|
|
2347
3062
|
v: 1,
|
|
2348
3063
|
items: [
|
|
2349
3064
|
{
|
|
2350
|
-
hashes:
|
|
2351
|
-
uris: [
|
|
3065
|
+
hashes: hashes2,
|
|
3066
|
+
uris: [uri],
|
|
2352
3067
|
enc: envelope
|
|
2353
3068
|
}
|
|
2354
3069
|
]
|
|
2355
3070
|
};
|
|
2356
3071
|
const recordBytes = await encodeRecord(record, input.signer);
|
|
2357
|
-
return postPublish(config,
|
|
3072
|
+
return postPublish(config, bytesToHex3(recordBytes), input.quoteId, input.idempotencyKey);
|
|
2358
3073
|
}
|
|
2359
3074
|
async function publishMerkle(config, input) {
|
|
2360
3075
|
if (input.signer !== void 0) assertSigner(input.signer);
|
|
@@ -2377,21 +3092,20 @@ async function publishMerkle(config, input) {
|
|
|
2377
3092
|
}
|
|
2378
3093
|
return bytes;
|
|
2379
3094
|
});
|
|
2380
|
-
const root =
|
|
3095
|
+
const root = cloneToOwnedBuffer2(merkleSha2256Root(leaves));
|
|
2381
3096
|
const leavesListCbor = encodeLeavesList({ leaves, root });
|
|
2382
|
-
const
|
|
2383
|
-
const uri = uploadsResp.uploads[0].uri;
|
|
3097
|
+
const uri = await uploadBlob(config, leavesListCbor, input.idempotencyKey);
|
|
2384
3098
|
const merkleEntry = {
|
|
2385
3099
|
alg: MERKLE_ALG_ID,
|
|
2386
3100
|
root,
|
|
2387
3101
|
leaf_count: leaves.length,
|
|
2388
|
-
uris: [
|
|
3102
|
+
uris: [uri]
|
|
2389
3103
|
};
|
|
2390
3104
|
const record = { v: 1, merkle: [merkleEntry] };
|
|
2391
3105
|
const recordBytes = await encodeRecord(record, input.signer);
|
|
2392
3106
|
const published = await postPublish(
|
|
2393
3107
|
config,
|
|
2394
|
-
|
|
3108
|
+
bytesToHex3(recordBytes),
|
|
2395
3109
|
input.quoteId,
|
|
2396
3110
|
input.idempotencyKey
|
|
2397
3111
|
);
|
|
@@ -2399,7 +3113,7 @@ async function publishMerkle(config, input) {
|
|
|
2399
3113
|
id: published.id,
|
|
2400
3114
|
tx_hash: published.tx_hash,
|
|
2401
3115
|
status: published.status,
|
|
2402
|
-
root:
|
|
3116
|
+
root: bytesToHex3(root),
|
|
2403
3117
|
leaf_count: leaves.length,
|
|
2404
3118
|
ar_uri: uri,
|
|
2405
3119
|
balance_after_usd_micros: published.balance_after_usd_micros
|
|
@@ -2494,6 +3208,65 @@ var PoeNamespace = class {
|
|
|
2494
3208
|
await throwIfNotOk(response);
|
|
2495
3209
|
return await readJson(response);
|
|
2496
3210
|
}
|
|
3211
|
+
/**
|
|
3212
|
+
* Upload a single file of any size, choosing the ingress path by size.
|
|
3213
|
+
*
|
|
3214
|
+
* A file at or below `threshold` (default ~48 MiB) is sent with the unchanged
|
|
3215
|
+
* single-shot `uploads()` multipart call. A larger file is uploaded as a
|
|
3216
|
+
* resumable, content-addressed session: the helper streams the whole-file
|
|
3217
|
+
* SHA-256 once (never buffering a multi-GB file), creates a session, PUTs each
|
|
3218
|
+
* chunk (several in parallel, retrying a failed chunk), then completes —
|
|
3219
|
+
* polling the shared attempt endpoint when completion is accepted
|
|
3220
|
+
* asynchronously. Both paths converge on one `ar://` URI.
|
|
3221
|
+
*
|
|
3222
|
+
* The chunk size is the server's authoritative `chunk_bytes` from the create
|
|
3223
|
+
* response, clamped to its `max_chunk_bytes` ceiling; the client's `chunkBytes`
|
|
3224
|
+
* is only a request. A create-time dedup hit returns the existing URI without
|
|
3225
|
+
* uploading; a `402` funding error is surfaced as a typed error.
|
|
3226
|
+
*
|
|
3227
|
+
* The `source` works in both runtimes: a `Blob`/`File` in the browser, a
|
|
3228
|
+
* `Uint8Array`, a filesystem path string, or a pre-adapted `ResumableSource`
|
|
3229
|
+
* on the server. To resume an interrupted upload, pass the prior `sessionId`;
|
|
3230
|
+
* the helper GETs its status and uploads only the missing chunks.
|
|
3231
|
+
*/
|
|
3232
|
+
async uploadResumable(input) {
|
|
3233
|
+
return uploadResumable(this.config, this.singleShotUpload, input);
|
|
3234
|
+
}
|
|
3235
|
+
/**
|
|
3236
|
+
* Upload exactly one blob via the single-shot multipart route and resolve its
|
|
3237
|
+
* `ar://` URI. Backs the small-file branch of `uploadResumable`; it shares the
|
|
3238
|
+
* `uploads()` wire shape but takes one blob and an optional abort signal, and
|
|
3239
|
+
* surfaces a per-file failure as a `PartialUploadError` (the resumable helper
|
|
3240
|
+
* promises a single resolved URI, unlike the raw `uploads()` passthrough).
|
|
3241
|
+
*/
|
|
3242
|
+
singleShotUpload = async ({
|
|
3243
|
+
target,
|
|
3244
|
+
bytes,
|
|
3245
|
+
idempotencyKey,
|
|
3246
|
+
signal
|
|
3247
|
+
}) => {
|
|
3248
|
+
const form = new FormData();
|
|
3249
|
+
form.append("target", target);
|
|
3250
|
+
form.append(
|
|
3251
|
+
"file_0",
|
|
3252
|
+
new Blob([bytes], { type: "application/octet-stream" }),
|
|
3253
|
+
"file_0.bin"
|
|
3254
|
+
);
|
|
3255
|
+
const response = await this.config.fetch(`${this.config.baseUrl}/api/v1/poe/uploads`, {
|
|
3256
|
+
method: "POST",
|
|
3257
|
+
headers: buildMultipartHeaders2({ apiKey: this.config.apiKey, idempotencyKey }),
|
|
3258
|
+
body: form,
|
|
3259
|
+
...signal ? { signal } : {}
|
|
3260
|
+
});
|
|
3261
|
+
await throwIfNotOk(response);
|
|
3262
|
+
const result = await readJson(response);
|
|
3263
|
+
const entry = result.uploads[0];
|
|
3264
|
+
if (entry === void 0 || entry.ok === false) {
|
|
3265
|
+
throw new PartialUploadError(result);
|
|
3266
|
+
}
|
|
3267
|
+
const ok = entry;
|
|
3268
|
+
return { uri: ok.uri, sha256: ok.sha256, bytes: ok.bytes };
|
|
3269
|
+
};
|
|
2497
3270
|
/**
|
|
2498
3271
|
* Submit a single finalised canonical-CBOR record to Cardano. Caller is
|
|
2499
3272
|
* responsible for constructing the record bytes (use `publishContent` /
|
|
@@ -2672,8 +3445,12 @@ var RecordsNamespace = class {
|
|
|
2672
3445
|
* between.
|
|
2673
3446
|
*
|
|
2674
3447
|
* Auth required (Bearer with `poe:read` scope, or NextAuth session
|
|
2675
|
-
* cookie).
|
|
2676
|
-
*
|
|
3448
|
+
* cookie). This is the hosted PUBLIC verifier: it accepts no decryption
|
|
3449
|
+
* credentials, and sealed items report as unverifiable without decryption.
|
|
3450
|
+
* To verify as a recipient (decrypt + plaintext-hash recheck), run the
|
|
3451
|
+
* `verifier` module locally with its `decryption` input — keys never leave
|
|
3452
|
+
* the process. Optional `fetch_content: false` skips content re-fetching;
|
|
3453
|
+
* affected claims report `not_checked`.
|
|
2677
3454
|
*/
|
|
2678
3455
|
async verify(txHash, input) {
|
|
2679
3456
|
const response = await this.config.fetch(
|
|
@@ -2744,6 +3521,8 @@ var Label309Client = class {
|
|
|
2744
3521
|
exports.AccountNamespace = AccountNamespace;
|
|
2745
3522
|
exports.BatchEmptyError = BatchEmptyError;
|
|
2746
3523
|
exports.BatchTooLargeError = BatchTooLargeError;
|
|
3524
|
+
exports.DEFAULT_RESUMABLE_CHUNK_BYTES = DEFAULT_RESUMABLE_CHUNK_BYTES;
|
|
3525
|
+
exports.DEFAULT_RESUMABLE_THRESHOLD_BYTES = DEFAULT_RESUMABLE_THRESHOLD_BYTES;
|
|
2747
3526
|
exports.ForbiddenError = ForbiddenError;
|
|
2748
3527
|
exports.IdempotencyConflictError = IdempotencyConflictError;
|
|
2749
3528
|
exports.InsufficientFundsError = InsufficientFundsError;
|
|
@@ -2765,6 +3544,7 @@ exports.QuoteNotFoundError = QuoteNotFoundError;
|
|
|
2765
3544
|
exports.RateLimitedError = RateLimitedError;
|
|
2766
3545
|
exports.RecordNotFoundError = RecordNotFoundError;
|
|
2767
3546
|
exports.RecordsNamespace = RecordsNamespace;
|
|
3547
|
+
exports.ResumableUploadError = ResumableUploadError;
|
|
2768
3548
|
exports.ServiceUnavailableError = ServiceUnavailableError;
|
|
2769
3549
|
exports.UnauthorizedError = UnauthorizedError;
|
|
2770
3550
|
exports.ValidationFailedError = ValidationFailedError;
|
|
@@ -2774,5 +3554,6 @@ exports.buildToSign = buildToSign;
|
|
|
2774
3554
|
exports.parseHttpError = parseHttpError;
|
|
2775
3555
|
exports.prepareSigStructure = prepareSigStructure;
|
|
2776
3556
|
exports.prepareSigStructureHashed = prepareSigStructureHashed;
|
|
3557
|
+
exports.toResumableSource = toResumableSource;
|
|
2777
3558
|
//# sourceMappingURL=index.cjs.map
|
|
2778
3559
|
//# sourceMappingURL=index.cjs.map
|