@cardanowall/sdk-ts 0.3.0 → 0.4.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 +1140 -363
- package/dist/client/index.cjs.map +1 -1
- package/dist/client/index.d.cts +42 -5
- package/dist/client/index.d.ts +42 -5
- package/dist/client/index.js +1138 -365
- 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 +5474 -2518
- 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 +5454 -2514
- 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-Dp4wUSFI.d.cts} +220 -1
- package/dist/{types-DGsZTMuZ.d.ts → types-Dp4wUSFI.d.ts} +220 -1
- 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.js
CHANGED
|
@@ -3,21 +3,21 @@ import { sortCoreDeterministic } from 'cbor2/sorts';
|
|
|
3
3
|
import { blake2b } from '@noble/hashes/blake2.js';
|
|
4
4
|
import * as ed from '@noble/ed25519';
|
|
5
5
|
import { sha512, sha256 as sha256$1 } from '@noble/hashes/sha2.js';
|
|
6
|
-
import 'hash-wasm';
|
|
6
|
+
import { createSHA256 } from 'hash-wasm';
|
|
7
7
|
import { z } from 'zod';
|
|
8
8
|
import { randomBytes as randomBytes$1 } from '@noble/ciphers/utils.js';
|
|
9
|
-
import {
|
|
10
|
-
import { xchacha20poly1305, chacha20poly1305 } from '@noble/ciphers/chacha.js';
|
|
11
|
-
import { hkdf } from '@noble/hashes/hkdf.js';
|
|
9
|
+
import { chacha20poly1305 } from '@noble/ciphers/chacha.js';
|
|
12
10
|
import '@noble/curves/abstract/edwards.js';
|
|
13
11
|
import '@noble/curves/abstract/montgomery.js';
|
|
14
12
|
import '@noble/curves/abstract/weierstrass.js';
|
|
15
13
|
import { x25519 } from '@noble/curves/ed25519.js';
|
|
16
14
|
import '@noble/curves/nist.js';
|
|
17
15
|
import { concatBytes, asciiToBytes, bytesToNumberLE, bytesToNumberBE } from '@noble/curves/utils.js';
|
|
16
|
+
import { hkdf } from '@noble/hashes/hkdf.js';
|
|
18
17
|
import { sha3_256, shake256, sha3_512, shake128 } from '@noble/hashes/sha3.js';
|
|
19
18
|
import { anumber, abytes, randomBytes as randomBytes$2, u32, swap32IfBE } from '@noble/hashes/utils.js';
|
|
20
19
|
import { FFTCore, reverseBits } from '@noble/curves/abstract/fft.js';
|
|
20
|
+
import { hmac } from '@noble/hashes/hmac.js';
|
|
21
21
|
|
|
22
22
|
// ../crypto-core/dist/cbor.js
|
|
23
23
|
function encodeCanonicalCbor(value) {
|
|
@@ -81,6 +81,14 @@ function encodeCoseSign1(args) {
|
|
|
81
81
|
function sha256(input) {
|
|
82
82
|
return sha256$1(input);
|
|
83
83
|
}
|
|
84
|
+
async function sha256Stream(source) {
|
|
85
|
+
const hasher = await createSHA256();
|
|
86
|
+
hasher.init();
|
|
87
|
+
for await (const chunk of source) {
|
|
88
|
+
hasher.update(chunk);
|
|
89
|
+
}
|
|
90
|
+
return hasher.digest("binary");
|
|
91
|
+
}
|
|
84
92
|
function blake2b256(input) {
|
|
85
93
|
return blake2b(input, { dkLen: 32 });
|
|
86
94
|
}
|
|
@@ -136,190 +144,325 @@ function mthRecursive(leaves, start, end) {
|
|
|
136
144
|
const right = mthRecursive(leaves, start + k, end);
|
|
137
145
|
return hashNode(left, right);
|
|
138
146
|
}
|
|
139
|
-
|
|
140
|
-
z.
|
|
141
|
-
|
|
142
|
-
})
|
|
143
|
-
|
|
144
|
-
var UTF8_ENCODER = new TextEncoder();
|
|
145
|
-
var UriChunkArraySchema = z.array(
|
|
146
|
-
z.string().refine(
|
|
147
|
-
(s) => {
|
|
148
|
-
const n = UTF8_ENCODER.encode(s).length;
|
|
149
|
-
return n >= 1 && n <= 64;
|
|
150
|
-
},
|
|
151
|
-
{ params: { code: "CHUNK_TOO_LARGE" } }
|
|
152
|
-
)
|
|
153
|
-
).min(1);
|
|
147
|
+
function textKeyedMap(inner) {
|
|
148
|
+
return z.custom((value) => !(value instanceof Uint8Array), {
|
|
149
|
+
message: "CBOR byte string present where a text-keyed map is required"
|
|
150
|
+
}).pipe(inner);
|
|
151
|
+
}
|
|
154
152
|
var HashDigestSchema = z.instanceof(Uint8Array);
|
|
155
153
|
var HashesMapSchema = z.record(z.string(), HashDigestSchema);
|
|
156
|
-
var
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
z.
|
|
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
|
-
|
|
154
|
+
var UriSchema = z.string();
|
|
155
|
+
var MerkleCommitSchema = textKeyedMap(
|
|
156
|
+
z.object({
|
|
157
|
+
alg: z.string(),
|
|
158
|
+
root: z.instanceof(Uint8Array),
|
|
159
|
+
leaf_count: z.union([z.number().int(), z.bigint()]),
|
|
160
|
+
uris: z.array(UriSchema).optional()
|
|
161
|
+
}).strict()
|
|
162
|
+
);
|
|
163
|
+
var SlotSchema = textKeyedMap(
|
|
164
|
+
z.object({
|
|
165
|
+
epk: z.instanceof(Uint8Array).optional(),
|
|
166
|
+
kem_ct: z.instanceof(Uint8Array).optional(),
|
|
167
|
+
wrap: z.instanceof(Uint8Array).optional()
|
|
168
|
+
})
|
|
169
|
+
);
|
|
170
|
+
textKeyedMap(
|
|
171
|
+
z.object({
|
|
172
|
+
m: z.union([z.number().int(), z.bigint()]),
|
|
173
|
+
t: z.union([z.number().int(), z.bigint()]),
|
|
174
|
+
p: z.union([z.number().int(), z.bigint()])
|
|
175
|
+
}).strict()
|
|
176
|
+
);
|
|
177
|
+
var PassphraseBlockSchema = textKeyedMap(
|
|
178
|
+
z.object({
|
|
179
|
+
alg: z.string(),
|
|
180
|
+
salt: z.instanceof(Uint8Array).superRefine((bytes, ctx) => {
|
|
181
|
+
if (bytes.length < 16) {
|
|
182
|
+
ctx.addIssue({
|
|
183
|
+
code: "custom",
|
|
184
|
+
path: [],
|
|
185
|
+
message: `passphrase.salt length ${bytes.length} < 16`,
|
|
186
|
+
params: { code: "ENC_PASSPHRASE_SALT_TOO_SHORT" }
|
|
187
|
+
});
|
|
188
|
+
} else if (bytes.length > 64) {
|
|
189
|
+
ctx.addIssue({
|
|
190
|
+
code: "custom",
|
|
191
|
+
path: [],
|
|
192
|
+
message: `passphrase.salt length ${bytes.length} > 64`,
|
|
193
|
+
params: { code: "ENC_PASSPHRASE_SALT_TOO_LONG" }
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
}),
|
|
197
|
+
params: z.record(z.string(), z.unknown())
|
|
198
|
+
}).strict()
|
|
199
|
+
);
|
|
200
|
+
var EncScheme1Schema = textKeyedMap(
|
|
201
|
+
z.object({
|
|
202
|
+
scheme: z.literal(1),
|
|
203
|
+
aead: z.string(),
|
|
204
|
+
kem: z.string().optional(),
|
|
205
|
+
nonce: z.instanceof(Uint8Array),
|
|
206
|
+
slots: z.array(SlotSchema).optional(),
|
|
207
|
+
slots_mac: z.instanceof(Uint8Array).refine((b) => b.length === 32, {
|
|
208
|
+
params: { code: "ENC_SLOTS_MAC_INVALID_LENGTH" }
|
|
209
|
+
}).optional(),
|
|
210
|
+
passphrase: PassphraseBlockSchema.optional()
|
|
211
|
+
}).strict()
|
|
212
|
+
);
|
|
213
|
+
var EncOpaqueSchema = textKeyedMap(
|
|
214
|
+
z.looseObject({
|
|
215
|
+
scheme: z.union([z.number().int().nonnegative(), z.bigint().nonnegative()])
|
|
216
|
+
})
|
|
217
|
+
);
|
|
218
|
+
z.union([EncScheme1Schema, EncOpaqueSchema]);
|
|
219
|
+
var ItemEntrySchema = textKeyedMap(
|
|
220
|
+
z.object({
|
|
221
|
+
hashes: HashesMapSchema,
|
|
222
|
+
uris: z.array(UriSchema).optional(),
|
|
223
|
+
// Captured as `unknown`: the envelope is a union whose disposition
|
|
224
|
+
// (typed scheme-1 vs opaque) depends on identifier support, so the
|
|
225
|
+
// validator's domain pass — not this schema — narrows it.
|
|
226
|
+
enc: z.unknown().optional()
|
|
227
|
+
}).strict()
|
|
228
|
+
);
|
|
229
|
+
var SigEntrySchema = textKeyedMap(
|
|
230
|
+
z.object({
|
|
231
|
+
cose_key: z.instanceof(Uint8Array).optional(),
|
|
232
|
+
cose_sign1: z.instanceof(Uint8Array)
|
|
233
|
+
}).strict()
|
|
234
|
+
);
|
|
216
235
|
var SupersedesSchema = z.instanceof(Uint8Array).refine((b) => b.length === 32, {
|
|
217
236
|
params: { code: "SUPERSEDES_TX_INVALID_LENGTH" }
|
|
218
237
|
});
|
|
219
238
|
var VersionLiteralSchema = z.literal(1);
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
239
|
+
textKeyedMap(
|
|
240
|
+
z.looseObject({
|
|
241
|
+
v: VersionLiteralSchema,
|
|
242
|
+
items: z.array(ItemEntrySchema).optional(),
|
|
243
|
+
merkle: z.array(MerkleCommitSchema).optional(),
|
|
244
|
+
supersedes: SupersedesSchema.optional(),
|
|
245
|
+
sigs: z.array(SigEntrySchema).optional(),
|
|
246
|
+
crit: z.array(z.string()).optional()
|
|
247
|
+
})
|
|
248
|
+
);
|
|
228
249
|
|
|
229
250
|
// ../poe-standard/src/encoder.ts
|
|
230
251
|
function encodePoeRecord(record) {
|
|
231
|
-
return encodeCanonicalCbor(
|
|
232
|
-
}
|
|
233
|
-
function encodeRecordBodyForSigning(record) {
|
|
234
|
-
const body = recordToCborInternal(
|
|
252
|
+
return encodeCanonicalCbor(toCborValue(
|
|
235
253
|
record,
|
|
236
254
|
/* includeSigs */
|
|
237
|
-
|
|
238
|
-
);
|
|
239
|
-
return encodeCanonicalCbor(body);
|
|
255
|
+
true
|
|
256
|
+
));
|
|
240
257
|
}
|
|
241
|
-
function
|
|
242
|
-
return
|
|
258
|
+
function encodeRecordBodyForSigning(record) {
|
|
259
|
+
return encodeCanonicalCbor(toCborValue(
|
|
243
260
|
record,
|
|
244
261
|
/* includeSigs */
|
|
245
|
-
|
|
246
|
-
);
|
|
247
|
-
}
|
|
248
|
-
function recordToCborInternal(record, includeSigs) {
|
|
249
|
-
const out = { v: record.v };
|
|
250
|
-
if (record.items !== void 0) out["items"] = record.items.map(itemToCbor);
|
|
251
|
-
if (record.merkle !== void 0) out["merkle"] = record.merkle.map(merkleToCbor);
|
|
252
|
-
if (record.supersedes !== void 0) out["supersedes"] = record.supersedes;
|
|
253
|
-
if (includeSigs && record.sigs !== void 0) out["sigs"] = record.sigs.map(sigEntryToCbor);
|
|
254
|
-
if (record.crit !== void 0) out["crit"] = record.crit.slice();
|
|
255
|
-
for (const [k, v] of Object.entries(record)) {
|
|
256
|
-
if (k === "v" || k === "items" || k === "merkle" || k === "supersedes" || k === "sigs" || k === "crit") {
|
|
257
|
-
continue;
|
|
258
|
-
}
|
|
259
|
-
out[k] = v;
|
|
260
|
-
}
|
|
261
|
-
return out;
|
|
262
|
-
}
|
|
263
|
-
function itemToCbor(item) {
|
|
264
|
-
const out = { hashes: hashesToCbor(item.hashes) };
|
|
265
|
-
if (item.uris !== void 0) {
|
|
266
|
-
out["uris"] = item.uris.map((chunks) => chunks.slice());
|
|
267
|
-
}
|
|
268
|
-
if (item.enc !== void 0) {
|
|
269
|
-
out["enc"] = envelopeToCbor(item.enc);
|
|
270
|
-
}
|
|
271
|
-
return out;
|
|
262
|
+
false
|
|
263
|
+
));
|
|
272
264
|
}
|
|
273
|
-
function
|
|
265
|
+
function toCborValue(record, includeSigs) {
|
|
274
266
|
const out = {};
|
|
275
|
-
for (const [
|
|
276
|
-
|
|
267
|
+
for (const [key, value] of Object.entries(record)) {
|
|
268
|
+
if (value === void 0) continue;
|
|
269
|
+
if (!includeSigs && key === "sigs") continue;
|
|
270
|
+
out[key] = stripUndefined(value);
|
|
277
271
|
}
|
|
278
272
|
return out;
|
|
279
273
|
}
|
|
280
|
-
function
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
root: commit.root,
|
|
284
|
-
leaf_count: commit.leaf_count
|
|
285
|
-
};
|
|
286
|
-
if (commit.uris !== void 0) {
|
|
287
|
-
out["uris"] = commit.uris.map((chunks) => chunks.slice());
|
|
274
|
+
function stripUndefined(value) {
|
|
275
|
+
if (value === null || typeof value !== "object" || value instanceof Uint8Array) {
|
|
276
|
+
return value;
|
|
288
277
|
}
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
function envelopeToCbor(enc) {
|
|
292
|
-
const out = {
|
|
293
|
-
scheme: enc.scheme,
|
|
294
|
-
aead: enc.aead,
|
|
295
|
-
nonce: enc.nonce
|
|
296
|
-
};
|
|
297
|
-
if (enc.kem !== void 0) out["kem"] = enc.kem;
|
|
298
|
-
if (enc.slots !== void 0) out["slots"] = enc.slots.map(slotToCbor);
|
|
299
|
-
if (enc.slots_mac !== void 0) out["slots_mac"] = enc.slots_mac;
|
|
300
|
-
if (enc.passphrase !== void 0) out["passphrase"] = passphraseToCbor(enc.passphrase);
|
|
301
|
-
return out;
|
|
302
|
-
}
|
|
303
|
-
function slotToCbor(slot) {
|
|
304
|
-
if (slot.kem_ct !== void 0) {
|
|
305
|
-
return { kem_ct: slot.kem_ct.map((c) => c), wrap: slot.wrap };
|
|
278
|
+
if (Array.isArray(value)) {
|
|
279
|
+
return value.map((element) => stripUndefined(element));
|
|
306
280
|
}
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
out["cose_key"] = entry.cose_key.map((b) => b);
|
|
281
|
+
if (value instanceof Map) {
|
|
282
|
+
const out2 = /* @__PURE__ */ new Map();
|
|
283
|
+
for (const [k, v] of value) {
|
|
284
|
+
if (v === void 0) continue;
|
|
285
|
+
out2.set(k, stripUndefined(v));
|
|
286
|
+
}
|
|
287
|
+
return out2;
|
|
288
|
+
}
|
|
289
|
+
const out = {};
|
|
290
|
+
for (const [k, v] of Object.entries(value)) {
|
|
291
|
+
if (v === void 0) continue;
|
|
292
|
+
out[k] = stripUndefined(v);
|
|
320
293
|
}
|
|
321
294
|
return out;
|
|
322
295
|
}
|
|
296
|
+
|
|
297
|
+
// ../poe-standard/src/error-codes.ts
|
|
298
|
+
var ERROR_CODES = [
|
|
299
|
+
"MALFORMED_CBOR",
|
|
300
|
+
"SCHEMA_TYPE_MISMATCH",
|
|
301
|
+
"SCHEMA_MISSING_REQUIRED",
|
|
302
|
+
"SCHEMA_UNKNOWN_FIELD",
|
|
303
|
+
"SCHEMA_INVALID_LITERAL",
|
|
304
|
+
"SCHEMA_EMPTY_RECORD",
|
|
305
|
+
"HASH_DIGEST_LENGTH_MISMATCH",
|
|
306
|
+
"UNSUPPORTED_HASH_ALG",
|
|
307
|
+
"UNSUPPORTED_MERKLE_COMMIT_ALG",
|
|
308
|
+
"SCHEMA_MERKLE_LEAF_COUNT_INVALID",
|
|
309
|
+
"INVALID_URI",
|
|
310
|
+
"CHUNK_TOO_LARGE",
|
|
311
|
+
"UNAUTHENTICATED_CIPHER_FORBIDDEN",
|
|
312
|
+
"UNSUPPORTED_AEAD_ALG",
|
|
313
|
+
"NONCE_LENGTH_MISMATCH",
|
|
314
|
+
"UNSUPPORTED_ENVELOPE_SCHEME",
|
|
315
|
+
"ENC_UNSUPPORTED",
|
|
316
|
+
"ENC_SLOTS_EMPTY",
|
|
317
|
+
"ENC_SLOT_INVALID_SHAPE",
|
|
318
|
+
"UNSUPPORTED_KEM_ALG",
|
|
319
|
+
"ENC_KEM_REQUIRED",
|
|
320
|
+
"KEM_EPK_LENGTH_MISMATCH",
|
|
321
|
+
"KEM_CT_LENGTH_MISMATCH",
|
|
322
|
+
"WRAP_LENGTH_MISMATCH",
|
|
323
|
+
"ENC_SLOTS_MAC_INVALID_LENGTH",
|
|
324
|
+
"ENC_SLOTS_MAC_REQUIRED",
|
|
325
|
+
"ENC_SLOTS_REQUIRED",
|
|
326
|
+
"ENC_SLOTS_DUPLICATE_KEM_MATERIAL",
|
|
327
|
+
"ENC_SLOTS_TOO_MANY",
|
|
328
|
+
"ENC_ENVELOPE_TOO_LARGE",
|
|
329
|
+
"ENC_EXCLUSIVITY_VIOLATION",
|
|
330
|
+
"ENC_NO_KEY_PATH",
|
|
331
|
+
"ENC_REQUIRES_CONTENT_HASH",
|
|
332
|
+
"ENC_PASSPHRASE_ALG_UNSUPPORTED",
|
|
333
|
+
"ENC_PASSPHRASE_SALT_TOO_SHORT",
|
|
334
|
+
"ENC_PASSPHRASE_SALT_TOO_LONG",
|
|
335
|
+
"ENC_PASSPHRASE_ARGON2_PARAMS_TOO_LOW",
|
|
336
|
+
"ENC_PASSPHRASE_PARAMS_EXCEED_POLICY",
|
|
337
|
+
"MALFORMED_SIG_COSE_SIGN1",
|
|
338
|
+
"SIGNATURE_UNSUPPORTED",
|
|
339
|
+
"SIG_ENTRY_INVALID_SHAPE",
|
|
340
|
+
"SIG_ENTRY_KID_COSE_KEY_CONFLICT",
|
|
341
|
+
"SIG_PRIVATE_KEY_LEAKED",
|
|
342
|
+
"SUPERSEDES_TX_INVALID_LENGTH",
|
|
343
|
+
"EXTENSION_UNSUPPORTED_CRITICAL",
|
|
344
|
+
"CRIT_SHAPE_INVALID",
|
|
345
|
+
"TX_NOT_FOUND",
|
|
346
|
+
"PROVIDER_UNAVAILABLE",
|
|
347
|
+
"TX_INTEGRITY_MISMATCH",
|
|
348
|
+
"METADATA_NOT_FOUND",
|
|
349
|
+
"INSUFFICIENT_CONFIRMATIONS",
|
|
350
|
+
"SIGNATURE_INVALID",
|
|
351
|
+
"SIGNER_KEY_UNRESOLVED",
|
|
352
|
+
"WALLET_ADDRESS_MISMATCH",
|
|
353
|
+
"URI_TARGET_FORBIDDEN",
|
|
354
|
+
"URI_INTEGRITY_MISMATCH",
|
|
355
|
+
"URI_PROVIDER_INTEGRITY_MISMATCH",
|
|
356
|
+
"URI_FETCH_FAILED",
|
|
357
|
+
"CONTENT_UNAVAILABLE",
|
|
358
|
+
"CONTENT_FETCH_LIMIT_EXCEEDED",
|
|
359
|
+
"CIPHERTEXT_UNAVAILABLE",
|
|
360
|
+
"SERVICE_INDEPENDENCE_VIOLATION",
|
|
361
|
+
"WRONG_DECRYPTION_INPUT_SHAPE",
|
|
362
|
+
"WRONG_RECIPIENT_KEY",
|
|
363
|
+
"TAMPERED_HEADER",
|
|
364
|
+
"TAMPERED_CIPHERTEXT",
|
|
365
|
+
"KDF_DERIVATION_FAILED",
|
|
366
|
+
"ENC_PASSPHRASE_UNNORMALIZABLE",
|
|
367
|
+
"ENC_PASSPHRASE_EMPTY",
|
|
368
|
+
"SCHEMA_MERKLE_LEAF_COUNT_MISMATCH",
|
|
369
|
+
"SCHEMA_MERKLE_LEAVES_FORMAT_UNSUPPORTED",
|
|
370
|
+
"SCHEMA_MERKLE_LEAVES_MALFORMED",
|
|
371
|
+
"MERKLE_ROOT_MISMATCH",
|
|
372
|
+
"MERKLE_LEAVES_UNAVAILABLE",
|
|
373
|
+
"MERKLE_UNSUPPORTED",
|
|
374
|
+
"OUT_OF_PROFILE_SKIPPED"
|
|
375
|
+
];
|
|
376
|
+
var ERROR_CODE_PART = Object.freeze({
|
|
377
|
+
MALFORMED_CBOR: "A",
|
|
378
|
+
SCHEMA_TYPE_MISMATCH: "A",
|
|
379
|
+
SCHEMA_MISSING_REQUIRED: "A",
|
|
380
|
+
SCHEMA_UNKNOWN_FIELD: "A",
|
|
381
|
+
SCHEMA_INVALID_LITERAL: "A",
|
|
382
|
+
SCHEMA_EMPTY_RECORD: "A",
|
|
383
|
+
HASH_DIGEST_LENGTH_MISMATCH: "A",
|
|
384
|
+
UNSUPPORTED_HASH_ALG: "A",
|
|
385
|
+
UNSUPPORTED_MERKLE_COMMIT_ALG: "A",
|
|
386
|
+
SCHEMA_MERKLE_LEAF_COUNT_INVALID: "A",
|
|
387
|
+
INVALID_URI: "A",
|
|
388
|
+
CHUNK_TOO_LARGE: "carriage",
|
|
389
|
+
UNAUTHENTICATED_CIPHER_FORBIDDEN: "A",
|
|
390
|
+
UNSUPPORTED_AEAD_ALG: "A",
|
|
391
|
+
NONCE_LENGTH_MISMATCH: "A",
|
|
392
|
+
UNSUPPORTED_ENVELOPE_SCHEME: "A",
|
|
393
|
+
ENC_UNSUPPORTED: "A",
|
|
394
|
+
ENC_SLOTS_EMPTY: "A",
|
|
395
|
+
ENC_SLOT_INVALID_SHAPE: "A",
|
|
396
|
+
UNSUPPORTED_KEM_ALG: "A",
|
|
397
|
+
ENC_KEM_REQUIRED: "A",
|
|
398
|
+
KEM_EPK_LENGTH_MISMATCH: "A",
|
|
399
|
+
KEM_CT_LENGTH_MISMATCH: "A",
|
|
400
|
+
WRAP_LENGTH_MISMATCH: "A",
|
|
401
|
+
ENC_SLOTS_MAC_INVALID_LENGTH: "A",
|
|
402
|
+
ENC_SLOTS_MAC_REQUIRED: "A",
|
|
403
|
+
ENC_SLOTS_REQUIRED: "A",
|
|
404
|
+
ENC_SLOTS_DUPLICATE_KEM_MATERIAL: "A",
|
|
405
|
+
ENC_SLOTS_TOO_MANY: "A",
|
|
406
|
+
ENC_ENVELOPE_TOO_LARGE: "A",
|
|
407
|
+
ENC_EXCLUSIVITY_VIOLATION: "A",
|
|
408
|
+
ENC_NO_KEY_PATH: "A",
|
|
409
|
+
ENC_REQUIRES_CONTENT_HASH: "A",
|
|
410
|
+
ENC_PASSPHRASE_ALG_UNSUPPORTED: "A",
|
|
411
|
+
ENC_PASSPHRASE_SALT_TOO_SHORT: "A",
|
|
412
|
+
ENC_PASSPHRASE_SALT_TOO_LONG: "A",
|
|
413
|
+
ENC_PASSPHRASE_ARGON2_PARAMS_TOO_LOW: "A",
|
|
414
|
+
ENC_PASSPHRASE_PARAMS_EXCEED_POLICY: "A",
|
|
415
|
+
MALFORMED_SIG_COSE_SIGN1: "A",
|
|
416
|
+
SIGNATURE_UNSUPPORTED: "A",
|
|
417
|
+
SIG_ENTRY_INVALID_SHAPE: "A",
|
|
418
|
+
SIG_ENTRY_KID_COSE_KEY_CONFLICT: "A",
|
|
419
|
+
SIG_PRIVATE_KEY_LEAKED: "A",
|
|
420
|
+
SUPERSEDES_TX_INVALID_LENGTH: "A",
|
|
421
|
+
EXTENSION_UNSUPPORTED_CRITICAL: "A",
|
|
422
|
+
CRIT_SHAPE_INVALID: "A",
|
|
423
|
+
TX_NOT_FOUND: "B",
|
|
424
|
+
PROVIDER_UNAVAILABLE: "B",
|
|
425
|
+
TX_INTEGRITY_MISMATCH: "B",
|
|
426
|
+
METADATA_NOT_FOUND: "B",
|
|
427
|
+
INSUFFICIENT_CONFIRMATIONS: "B",
|
|
428
|
+
SIGNATURE_INVALID: "B",
|
|
429
|
+
SIGNER_KEY_UNRESOLVED: "B",
|
|
430
|
+
WALLET_ADDRESS_MISMATCH: "B",
|
|
431
|
+
URI_TARGET_FORBIDDEN: "B",
|
|
432
|
+
URI_INTEGRITY_MISMATCH: "B",
|
|
433
|
+
URI_PROVIDER_INTEGRITY_MISMATCH: "B",
|
|
434
|
+
URI_FETCH_FAILED: "B",
|
|
435
|
+
CONTENT_UNAVAILABLE: "B",
|
|
436
|
+
CONTENT_FETCH_LIMIT_EXCEEDED: "B",
|
|
437
|
+
CIPHERTEXT_UNAVAILABLE: "B",
|
|
438
|
+
SERVICE_INDEPENDENCE_VIOLATION: "B",
|
|
439
|
+
WRONG_DECRYPTION_INPUT_SHAPE: "B",
|
|
440
|
+
WRONG_RECIPIENT_KEY: "B",
|
|
441
|
+
TAMPERED_HEADER: "B",
|
|
442
|
+
TAMPERED_CIPHERTEXT: "B",
|
|
443
|
+
KDF_DERIVATION_FAILED: "B",
|
|
444
|
+
ENC_PASSPHRASE_UNNORMALIZABLE: "B",
|
|
445
|
+
ENC_PASSPHRASE_EMPTY: "B",
|
|
446
|
+
SCHEMA_MERKLE_LEAF_COUNT_MISMATCH: "B",
|
|
447
|
+
SCHEMA_MERKLE_LEAVES_FORMAT_UNSUPPORTED: "B",
|
|
448
|
+
SCHEMA_MERKLE_LEAVES_MALFORMED: "B",
|
|
449
|
+
MERKLE_ROOT_MISMATCH: "B",
|
|
450
|
+
MERKLE_LEAVES_UNAVAILABLE: "B",
|
|
451
|
+
MERKLE_UNSUPPORTED: "B",
|
|
452
|
+
OUT_OF_PROFILE_SKIPPED: "B"
|
|
453
|
+
});
|
|
454
|
+
Object.freeze(
|
|
455
|
+
ERROR_CODES.filter((code) => ERROR_CODE_PART[code] === "A")
|
|
456
|
+
);
|
|
457
|
+
Object.freeze(
|
|
458
|
+
ERROR_CODES.filter((code) => ERROR_CODE_PART[code] === "carriage")
|
|
459
|
+
);
|
|
460
|
+
Object.freeze(
|
|
461
|
+
ERROR_CODES.filter((code) => ERROR_CODE_PART[code] === "B")
|
|
462
|
+
);
|
|
463
|
+
new Map(
|
|
464
|
+
ERROR_CODES.map((code, index) => [code, index])
|
|
465
|
+
);
|
|
323
466
|
var abytesDoc = abytes;
|
|
324
467
|
var randomBytes = randomBytes$2;
|
|
325
468
|
function equalBytes(a, b) {
|
|
@@ -984,12 +1127,6 @@ var XWing = /* @__PURE__ */ (() => ml_kem768_x25519)();
|
|
|
984
1127
|
function chacha20Poly1305Encrypt(opts2) {
|
|
985
1128
|
return chacha20poly1305(opts2.key, opts2.nonce, opts2.aad).encrypt(opts2.plaintext);
|
|
986
1129
|
}
|
|
987
|
-
function xchacha20Poly1305Encrypt(opts2) {
|
|
988
|
-
return xchacha20poly1305(opts2.key, opts2.nonce, opts2.aad).encrypt(opts2.plaintext);
|
|
989
|
-
}
|
|
990
|
-
function hkdfSha256(opts2) {
|
|
991
|
-
return hkdf(sha256$1, opts2.ikm, opts2.salt, opts2.info, opts2.length);
|
|
992
|
-
}
|
|
993
1130
|
var MLKEM768X25519_PUBLIC_KEY_LENGTH = 1216;
|
|
994
1131
|
var MLKEM768X25519_ENC_LENGTH = 1120;
|
|
995
1132
|
var MLKEM768X25519_ESEED_LENGTH = 64;
|
|
@@ -1028,6 +1165,9 @@ function x25519Ecdh(opts2) {
|
|
|
1028
1165
|
throw e;
|
|
1029
1166
|
}
|
|
1030
1167
|
}
|
|
1168
|
+
function hkdfSha256(opts2) {
|
|
1169
|
+
return hkdf(sha256$1, opts2.ikm, opts2.salt, opts2.info, opts2.length);
|
|
1170
|
+
}
|
|
1031
1171
|
var EciesSealedPoeError = class extends Error {
|
|
1032
1172
|
code;
|
|
1033
1173
|
constructor(code, message, options) {
|
|
@@ -1036,37 +1176,98 @@ var EciesSealedPoeError = class extends Error {
|
|
|
1036
1176
|
this.code = code;
|
|
1037
1177
|
}
|
|
1038
1178
|
};
|
|
1039
|
-
var
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1179
|
+
var CHUNK_SIZE = 65536;
|
|
1180
|
+
var TAG_SIZE = 16;
|
|
1181
|
+
var NONCE_LENGTH = 12;
|
|
1182
|
+
var COUNTER_LENGTH = 11;
|
|
1183
|
+
var PAYLOAD_KEY_LENGTH = 32;
|
|
1184
|
+
var EMPTY_AAD = new Uint8Array(0);
|
|
1185
|
+
var ChunkNonce = class {
|
|
1186
|
+
nonce = new Uint8Array(NONCE_LENGTH);
|
|
1187
|
+
finished = false;
|
|
1188
|
+
next(final) {
|
|
1189
|
+
if (this.finished) {
|
|
1190
|
+
throw new Error("STREAM: no chunks may follow the final chunk");
|
|
1191
|
+
}
|
|
1192
|
+
if (final) {
|
|
1193
|
+
this.finished = true;
|
|
1194
|
+
this.nonce[COUNTER_LENGTH] = 1;
|
|
1195
|
+
}
|
|
1196
|
+
const out = this.nonce.slice();
|
|
1197
|
+
this.increment();
|
|
1198
|
+
return out;
|
|
1199
|
+
}
|
|
1200
|
+
get done() {
|
|
1201
|
+
return this.finished;
|
|
1043
1202
|
}
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1203
|
+
increment() {
|
|
1204
|
+
for (let i = COUNTER_LENGTH - 1; i >= 0; i--) {
|
|
1205
|
+
const v = this.nonce[i] + 1 & 255;
|
|
1206
|
+
this.nonce[i] = v;
|
|
1207
|
+
if (v !== 0) return;
|
|
1208
|
+
}
|
|
1209
|
+
throw new Error("STREAM: chunk counter overflow");
|
|
1210
|
+
}
|
|
1211
|
+
};
|
|
1212
|
+
function assertPayloadKey(payloadKey) {
|
|
1213
|
+
if (payloadKey.length !== PAYLOAD_KEY_LENGTH) {
|
|
1214
|
+
throw new Error(
|
|
1215
|
+
`STREAM: payloadKey MUST be exactly ${PAYLOAD_KEY_LENGTH} bytes, got ${payloadKey.length}`
|
|
1216
|
+
);
|
|
1047
1217
|
}
|
|
1048
|
-
return chunks;
|
|
1049
1218
|
}
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1219
|
+
var StreamSealer = class {
|
|
1220
|
+
payloadKey;
|
|
1221
|
+
nonce = new ChunkNonce();
|
|
1222
|
+
chunkIndex = 0;
|
|
1223
|
+
constructor(payloadKey) {
|
|
1224
|
+
assertPayloadKey(payloadKey);
|
|
1225
|
+
this.payloadKey = payloadKey;
|
|
1226
|
+
}
|
|
1227
|
+
sealChunk(plaintext, final) {
|
|
1228
|
+
if (!final && plaintext.length !== CHUNK_SIZE) {
|
|
1229
|
+
throw new Error(
|
|
1230
|
+
`STREAM: non-final chunk MUST carry exactly ${CHUNK_SIZE} plaintext bytes, got ${plaintext.length}`
|
|
1231
|
+
);
|
|
1232
|
+
}
|
|
1233
|
+
if (final && plaintext.length > CHUNK_SIZE) {
|
|
1234
|
+
throw new Error(
|
|
1235
|
+
`STREAM: final chunk MUST carry at most ${CHUNK_SIZE} plaintext bytes, got ${plaintext.length}`
|
|
1236
|
+
);
|
|
1237
|
+
}
|
|
1238
|
+
if (final && plaintext.length === 0 && this.chunkIndex > 0) {
|
|
1239
|
+
throw new Error(
|
|
1240
|
+
"STREAM: a zero-length final chunk is admissible only for an empty plaintext"
|
|
1241
|
+
);
|
|
1242
|
+
}
|
|
1243
|
+
const sealed = chacha20Poly1305Encrypt({
|
|
1244
|
+
key: this.payloadKey,
|
|
1245
|
+
nonce: this.nonce.next(final),
|
|
1246
|
+
aad: EMPTY_AAD,
|
|
1247
|
+
plaintext
|
|
1248
|
+
});
|
|
1249
|
+
this.chunkIndex += 1;
|
|
1250
|
+
return sealed;
|
|
1251
|
+
}
|
|
1252
|
+
};
|
|
1253
|
+
function streamSeal(args) {
|
|
1254
|
+
const { plaintext } = args;
|
|
1255
|
+
const sealer = new StreamSealer(args.payloadKey);
|
|
1256
|
+
const chunkCount = Math.max(1, Math.ceil(plaintext.length / CHUNK_SIZE));
|
|
1257
|
+
const out = new Uint8Array(plaintext.length + chunkCount * TAG_SIZE);
|
|
1054
1258
|
let offset = 0;
|
|
1055
|
-
for (
|
|
1056
|
-
|
|
1057
|
-
|
|
1259
|
+
for (let i = 0; i < chunkCount; i++) {
|
|
1260
|
+
const final = i === chunkCount - 1;
|
|
1261
|
+
const chunk = plaintext.subarray(
|
|
1262
|
+
i * CHUNK_SIZE,
|
|
1263
|
+
Math.min((i + 1) * CHUNK_SIZE, plaintext.length)
|
|
1264
|
+
);
|
|
1265
|
+
const sealed = sealer.sealChunk(chunk, final);
|
|
1266
|
+
out.set(sealed, offset);
|
|
1267
|
+
offset += sealed.length;
|
|
1058
1268
|
}
|
|
1059
1269
|
return out;
|
|
1060
1270
|
}
|
|
1061
|
-
function canonicalizeSlots(slots, kem) {
|
|
1062
|
-
if (kem === "x25519") {
|
|
1063
|
-
return slots.map((s) => ({ epk: s.epk, wrap: s.wrap }));
|
|
1064
|
-
}
|
|
1065
|
-
return slots.map((s) => ({
|
|
1066
|
-
kem_ct: chunkKemCt(joinKemCt(s.kem_ct)),
|
|
1067
|
-
wrap: s.wrap
|
|
1068
|
-
}));
|
|
1069
|
-
}
|
|
1070
1271
|
function encodeCanonicalCbor3(value) {
|
|
1071
1272
|
return encode(value, {
|
|
1072
1273
|
cde: true,
|
|
@@ -1075,23 +1276,54 @@ function encodeCanonicalCbor3(value) {
|
|
|
1075
1276
|
sortKeys: sortCoreDeterministic
|
|
1076
1277
|
});
|
|
1077
1278
|
}
|
|
1279
|
+
var CARDANO_POE_ITEM_HASHES_PREFIX = new TextEncoder().encode(
|
|
1280
|
+
"cardano-poe-item-hashes-v1"
|
|
1281
|
+
);
|
|
1078
1282
|
var CARDANO_POE_SLOTS_TRANSCRIPT_PREFIX = new TextEncoder().encode(
|
|
1079
1283
|
"cardano-poe-slots-transcript-v1"
|
|
1080
1284
|
);
|
|
1285
|
+
var CARDANO_POE_PASSPHRASE_TRANSCRIPT_PREFIX = new TextEncoder().encode(
|
|
1286
|
+
"cardano-poe-passphrase-transcript-v1"
|
|
1287
|
+
);
|
|
1288
|
+
var CARDANO_POE_HKDF_INFO_SLOTS_MAC = new TextEncoder().encode(
|
|
1289
|
+
"cardano-poe-slots-mac-v1"
|
|
1290
|
+
);
|
|
1291
|
+
var CARDANO_POE_HKDF_INFO_PASSPHRASE_MAC = new TextEncoder().encode(
|
|
1292
|
+
"cardano-poe-passphrase-mac-v1"
|
|
1293
|
+
);
|
|
1081
1294
|
var CARDANO_POE_HKDF_INFO_PAYLOAD = new TextEncoder().encode(
|
|
1082
1295
|
"cardano-poe-payload-v1"
|
|
1083
1296
|
);
|
|
1084
1297
|
var CARDANO_POE_HKDF_INFO_PAYLOAD_PASSPHRASE = new TextEncoder().encode(
|
|
1085
1298
|
"cardano-poe-payload-passphrase-v1"
|
|
1086
1299
|
);
|
|
1300
|
+
var CARDANO_POE_X25519_KEK_SALT_PREFIX = new TextEncoder().encode(
|
|
1301
|
+
"cardano-poe-x25519-kek-salt-v1"
|
|
1302
|
+
);
|
|
1087
1303
|
var CARDANO_POE_XWING_KEK_SALT_PREFIX = new TextEncoder().encode(
|
|
1088
1304
|
"cardano-poe-xwing-kek-salt-v1"
|
|
1089
1305
|
);
|
|
1306
|
+
if (CARDANO_POE_ITEM_HASHES_PREFIX.length !== 26) {
|
|
1307
|
+
throw new Error("CARDANO_POE_ITEM_HASHES_PREFIX byte-length invariant violated (expected 26)");
|
|
1308
|
+
}
|
|
1090
1309
|
if (CARDANO_POE_SLOTS_TRANSCRIPT_PREFIX.length !== 31) {
|
|
1091
1310
|
throw new Error(
|
|
1092
1311
|
"CARDANO_POE_SLOTS_TRANSCRIPT_PREFIX byte-length invariant violated (expected 31)"
|
|
1093
1312
|
);
|
|
1094
1313
|
}
|
|
1314
|
+
if (CARDANO_POE_PASSPHRASE_TRANSCRIPT_PREFIX.length !== 36) {
|
|
1315
|
+
throw new Error(
|
|
1316
|
+
"CARDANO_POE_PASSPHRASE_TRANSCRIPT_PREFIX byte-length invariant violated (expected 36)"
|
|
1317
|
+
);
|
|
1318
|
+
}
|
|
1319
|
+
if (CARDANO_POE_HKDF_INFO_SLOTS_MAC.length !== 24) {
|
|
1320
|
+
throw new Error("CARDANO_POE_HKDF_INFO_SLOTS_MAC byte-length invariant violated (expected 24)");
|
|
1321
|
+
}
|
|
1322
|
+
if (CARDANO_POE_HKDF_INFO_PASSPHRASE_MAC.length !== 29) {
|
|
1323
|
+
throw new Error(
|
|
1324
|
+
"CARDANO_POE_HKDF_INFO_PASSPHRASE_MAC byte-length invariant violated (expected 29)"
|
|
1325
|
+
);
|
|
1326
|
+
}
|
|
1095
1327
|
if (CARDANO_POE_HKDF_INFO_PAYLOAD.length !== 22) {
|
|
1096
1328
|
throw new Error("CARDANO_POE_HKDF_INFO_PAYLOAD byte-length invariant violated (expected 22)");
|
|
1097
1329
|
}
|
|
@@ -1100,49 +1332,60 @@ if (CARDANO_POE_HKDF_INFO_PAYLOAD_PASSPHRASE.length !== 33) {
|
|
|
1100
1332
|
"CARDANO_POE_HKDF_INFO_PAYLOAD_PASSPHRASE byte-length invariant violated (expected 33)"
|
|
1101
1333
|
);
|
|
1102
1334
|
}
|
|
1335
|
+
if (CARDANO_POE_X25519_KEK_SALT_PREFIX.length !== 30) {
|
|
1336
|
+
throw new Error(
|
|
1337
|
+
"CARDANO_POE_X25519_KEK_SALT_PREFIX byte-length invariant violated (expected 30)"
|
|
1338
|
+
);
|
|
1339
|
+
}
|
|
1103
1340
|
if (CARDANO_POE_XWING_KEK_SALT_PREFIX.length !== 29) {
|
|
1104
1341
|
throw new Error("CARDANO_POE_XWING_KEK_SALT_PREFIX byte-length invariant violated (expected 29)");
|
|
1105
1342
|
}
|
|
1106
|
-
var
|
|
1107
|
-
function
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1343
|
+
var EMPTY_SALT = new Uint8Array(0);
|
|
1344
|
+
function labelledSha256(prefix, ...parts) {
|
|
1345
|
+
let total = prefix.length;
|
|
1346
|
+
for (const p of parts) total += p.length;
|
|
1347
|
+
const message = new Uint8Array(total);
|
|
1348
|
+
message.set(prefix, 0);
|
|
1349
|
+
let offset = prefix.length;
|
|
1350
|
+
for (const p of parts) {
|
|
1351
|
+
message.set(p, offset);
|
|
1352
|
+
offset += p.length;
|
|
1112
1353
|
}
|
|
1354
|
+
return sha256$1(message);
|
|
1113
1355
|
}
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1356
|
+
function itemHashesHash(hashes2) {
|
|
1357
|
+
if (Object.keys(hashes2).length === 0) {
|
|
1358
|
+
throw new EciesSealedPoeError(
|
|
1359
|
+
"ENC_REQUIRES_CONTENT_HASH",
|
|
1360
|
+
"hashes MUST carry at least one content-hash entry"
|
|
1361
|
+
);
|
|
1118
1362
|
}
|
|
1119
|
-
|
|
1363
|
+
return labelledSha256(CARDANO_POE_ITEM_HASHES_PREFIX, encodeCanonicalCbor3(hashes2));
|
|
1364
|
+
}
|
|
1120
1365
|
function computeSlotsHash(args) {
|
|
1366
|
+
const slots = args.kem === "x25519" ? args.slots.map((s) => ({ epk: s.epk, wrap: s.wrap })) : args.slots.map((s) => ({
|
|
1367
|
+
kem_ct: s.kem_ct,
|
|
1368
|
+
wrap: s.wrap
|
|
1369
|
+
}));
|
|
1121
1370
|
const transcript = {
|
|
1122
1371
|
scheme: 1,
|
|
1123
1372
|
path: "slots",
|
|
1124
|
-
aead:
|
|
1373
|
+
aead: args.aead,
|
|
1125
1374
|
kem: args.kem,
|
|
1126
1375
|
nonce: args.nonce,
|
|
1127
|
-
slots
|
|
1376
|
+
slots,
|
|
1377
|
+
hashes_hash: args.hashesHash
|
|
1128
1378
|
};
|
|
1129
|
-
|
|
1130
|
-
const message = new Uint8Array(CARDANO_POE_SLOTS_TRANSCRIPT_PREFIX.length + encoded.length);
|
|
1131
|
-
message.set(CARDANO_POE_SLOTS_TRANSCRIPT_PREFIX, 0);
|
|
1132
|
-
message.set(encoded, CARDANO_POE_SLOTS_TRANSCRIPT_PREFIX.length);
|
|
1133
|
-
return sha256$1(message);
|
|
1379
|
+
return labelledSha256(CARDANO_POE_SLOTS_TRANSCRIPT_PREFIX, encodeCanonicalCbor3(transcript));
|
|
1134
1380
|
}
|
|
1135
|
-
function
|
|
1136
|
-
const
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
slots_mac: args.slotsMac
|
|
1144
|
-
};
|
|
1145
|
-
return encodeCanonicalCbor3(ad);
|
|
1381
|
+
function computeSlotsMac(args) {
|
|
1382
|
+
const macKey = hkdfSha256({
|
|
1383
|
+
ikm: args.cek,
|
|
1384
|
+
salt: EMPTY_SALT,
|
|
1385
|
+
info: CARDANO_POE_HKDF_INFO_SLOTS_MAC,
|
|
1386
|
+
length: 32
|
|
1387
|
+
});
|
|
1388
|
+
return hmac(sha256$1, macKey, args.slotsHash);
|
|
1146
1389
|
}
|
|
1147
1390
|
function slotsPayloadKey(args) {
|
|
1148
1391
|
return hkdfSha256({
|
|
@@ -1152,31 +1395,22 @@ function slotsPayloadKey(args) {
|
|
|
1152
1395
|
length: 32
|
|
1153
1396
|
});
|
|
1154
1397
|
}
|
|
1398
|
+
function x25519KekSalt(args) {
|
|
1399
|
+
return labelledSha256(CARDANO_POE_X25519_KEK_SALT_PREFIX, args.nonce, args.epk, args.pubR);
|
|
1400
|
+
}
|
|
1155
1401
|
function xwingKekSalt(args) {
|
|
1156
|
-
|
|
1157
|
-
CARDANO_POE_XWING_KEK_SALT_PREFIX.length + args.kemCt.length + args.pubR.length
|
|
1158
|
-
);
|
|
1159
|
-
let offset = 0;
|
|
1160
|
-
message.set(CARDANO_POE_XWING_KEK_SALT_PREFIX, offset);
|
|
1161
|
-
offset += CARDANO_POE_XWING_KEK_SALT_PREFIX.length;
|
|
1162
|
-
message.set(args.kemCt, offset);
|
|
1163
|
-
offset += args.kemCt.length;
|
|
1164
|
-
message.set(args.pubR, offset);
|
|
1165
|
-
return sha256$1(message);
|
|
1402
|
+
return labelledSha256(CARDANO_POE_XWING_KEK_SALT_PREFIX, args.nonce, args.kemCt, args.pubR);
|
|
1166
1403
|
}
|
|
1404
|
+
var SEALED_POE_AEAD = "chacha20-poly1305-stream64k";
|
|
1167
1405
|
var CARDANO_POE_HKDF_INFO_KEK = new TextEncoder().encode("cardano-poe-kek-v1");
|
|
1168
1406
|
var CARDANO_POE_HKDF_INFO_KEK_MLKEM768X25519 = new TextEncoder().encode(
|
|
1169
1407
|
"cardano-poe-kek-mlkem768x25519-v1"
|
|
1170
1408
|
);
|
|
1171
|
-
var CARDANO_POE_HKDF_INFO_SLOTS_MAC = new TextEncoder().encode(
|
|
1172
|
-
"cardano-poe-slots-mac-v1"
|
|
1173
|
-
);
|
|
1174
1409
|
var ZERO_NONCE_12 = new Uint8Array(12);
|
|
1175
|
-
var EMPTY_SALT = new Uint8Array(0);
|
|
1176
1410
|
var X25519_PUBLIC_KEY_LENGTH = 32;
|
|
1177
1411
|
var X25519_SECRET_KEY_LENGTH = 32;
|
|
1178
1412
|
var CEK_LENGTH = 32;
|
|
1179
|
-
var
|
|
1413
|
+
var NONCE_LENGTH2 = 24;
|
|
1180
1414
|
var WRAP_LENGTH = 48;
|
|
1181
1415
|
var SLOTS_MAC_LENGTH = 32;
|
|
1182
1416
|
if (CARDANO_POE_HKDF_INFO_KEK.length !== 18) {
|
|
@@ -1187,18 +1421,9 @@ if (CARDANO_POE_HKDF_INFO_KEK_MLKEM768X25519.length !== 33) {
|
|
|
1187
1421
|
"CARDANO_POE_HKDF_INFO_KEK_MLKEM768X25519 byte-length invariant violated (expected 33)"
|
|
1188
1422
|
);
|
|
1189
1423
|
}
|
|
1190
|
-
if (CARDANO_POE_HKDF_INFO_SLOTS_MAC.length !== 24) {
|
|
1191
|
-
throw new Error("CARDANO_POE_HKDF_INFO_SLOTS_MAC byte-length invariant violated (expected 24)");
|
|
1192
|
-
}
|
|
1193
1424
|
if (ZERO_NONCE_12.length !== 12) {
|
|
1194
1425
|
throw new Error("ZERO_NONCE_12 byte-length invariant violated (expected 12)");
|
|
1195
1426
|
}
|
|
1196
|
-
function concat(a, b) {
|
|
1197
|
-
const out = new Uint8Array(a.length + b.length);
|
|
1198
|
-
out.set(a, 0);
|
|
1199
|
-
out.set(b, a.length);
|
|
1200
|
-
return out;
|
|
1201
|
-
}
|
|
1202
1427
|
function uniformIndexBelow(m) {
|
|
1203
1428
|
const limit = 4294967296 - 4294967296 % m;
|
|
1204
1429
|
const buf = new Uint32Array(1);
|
|
@@ -1229,7 +1454,7 @@ function wrapSlotX25519(args) {
|
|
|
1229
1454
|
const shared = x25519Ecdh({ secretKey: privEph, theirPublicKey: args.pubR });
|
|
1230
1455
|
const kek = hkdfSha256({
|
|
1231
1456
|
ikm: shared,
|
|
1232
|
-
salt:
|
|
1457
|
+
salt: x25519KekSalt({ nonce: args.nonce, epk, pubR: args.pubR }),
|
|
1233
1458
|
info: CARDANO_POE_HKDF_INFO_KEK,
|
|
1234
1459
|
length: 32
|
|
1235
1460
|
});
|
|
@@ -1254,7 +1479,7 @@ function wrapSlotMlkem768X25519(args) {
|
|
|
1254
1479
|
}
|
|
1255
1480
|
const kek = hkdfSha256({
|
|
1256
1481
|
ikm: ss,
|
|
1257
|
-
salt: xwingKekSalt({ kemCt: enc, pubR: args.pubR }),
|
|
1482
|
+
salt: xwingKekSalt({ nonce: args.nonce, kemCt: enc, pubR: args.pubR }),
|
|
1258
1483
|
info: CARDANO_POE_HKDF_INFO_KEK_MLKEM768X25519,
|
|
1259
1484
|
length: 32
|
|
1260
1485
|
});
|
|
@@ -1267,13 +1492,13 @@ function wrapSlotMlkem768X25519(args) {
|
|
|
1267
1492
|
if (wrap.length !== WRAP_LENGTH) {
|
|
1268
1493
|
throw new Error(`internal: wrap.length=${wrap.length}, expected ${WRAP_LENGTH}`);
|
|
1269
1494
|
}
|
|
1270
|
-
return { kem_ct:
|
|
1495
|
+
return { kem_ct: enc, wrap };
|
|
1271
1496
|
}
|
|
1272
1497
|
function eciesSealedPoeWrap(args) {
|
|
1273
1498
|
const { plaintext, recipientPublicKeys } = args;
|
|
1274
1499
|
const kem = args.kem ?? "x25519";
|
|
1275
1500
|
const n = recipientPublicKeys.length;
|
|
1276
|
-
|
|
1501
|
+
const hashesHash = itemHashesHash(args.hashes);
|
|
1277
1502
|
if (n < 1) {
|
|
1278
1503
|
throw new EciesSealedPoeError(
|
|
1279
1504
|
"ENC_SLOTS_EMPTY",
|
|
@@ -1329,21 +1554,20 @@ function eciesSealedPoeWrap(args) {
|
|
|
1329
1554
|
}
|
|
1330
1555
|
}
|
|
1331
1556
|
const cek = args.cek ?? randomBytes$1(CEK_LENGTH);
|
|
1332
|
-
const nonce = args.nonce ?? randomBytes$1(
|
|
1557
|
+
const nonce = args.nonce ?? randomBytes$1(NONCE_LENGTH2);
|
|
1333
1558
|
if (cek.length !== CEK_LENGTH) {
|
|
1334
1559
|
throw new EciesSealedPoeError(
|
|
1335
1560
|
"INVALID_CEK_LENGTH",
|
|
1336
1561
|
`cek MUST be exactly ${CEK_LENGTH} bytes, got ${cek.length}`
|
|
1337
1562
|
);
|
|
1338
1563
|
}
|
|
1339
|
-
if (nonce.length !==
|
|
1564
|
+
if (nonce.length !== NONCE_LENGTH2) {
|
|
1340
1565
|
throw new EciesSealedPoeError(
|
|
1341
1566
|
"NONCE_LENGTH_MISMATCH",
|
|
1342
|
-
`nonce MUST be exactly ${
|
|
1567
|
+
`nonce MUST be exactly ${NONCE_LENGTH2} bytes, got ${nonce.length}`
|
|
1343
1568
|
);
|
|
1344
1569
|
}
|
|
1345
1570
|
let envelope;
|
|
1346
|
-
let slotsHash;
|
|
1347
1571
|
if (kem === "x25519") {
|
|
1348
1572
|
const slots = [];
|
|
1349
1573
|
for (let i = 0; i < n; i++) {
|
|
@@ -1352,6 +1576,7 @@ function eciesSealedPoeWrap(args) {
|
|
|
1352
1576
|
pubR: recipientPublicKeys[i],
|
|
1353
1577
|
privEph: args.ephemeralSecrets ? args.ephemeralSecrets[i] : void 0,
|
|
1354
1578
|
cek,
|
|
1579
|
+
nonce,
|
|
1355
1580
|
slotIdx: i
|
|
1356
1581
|
})
|
|
1357
1582
|
);
|
|
@@ -1359,14 +1584,20 @@ function eciesSealedPoeWrap(args) {
|
|
|
1359
1584
|
if (args.skipShuffle !== true) {
|
|
1360
1585
|
csprngShuffle(slots);
|
|
1361
1586
|
}
|
|
1362
|
-
slotsHash = computeSlotsHash({
|
|
1587
|
+
const slotsHash = computeSlotsHash({
|
|
1588
|
+
aead: SEALED_POE_AEAD,
|
|
1589
|
+
kem: "x25519",
|
|
1590
|
+
nonce,
|
|
1591
|
+
slots,
|
|
1592
|
+
hashesHash
|
|
1593
|
+
});
|
|
1363
1594
|
envelope = {
|
|
1364
1595
|
scheme: 1,
|
|
1365
|
-
aead:
|
|
1596
|
+
aead: SEALED_POE_AEAD,
|
|
1366
1597
|
kem: "x25519",
|
|
1367
1598
|
nonce,
|
|
1368
1599
|
slots,
|
|
1369
|
-
slots_mac:
|
|
1600
|
+
slots_mac: sizedSlotsMac(cek, slotsHash)
|
|
1370
1601
|
};
|
|
1371
1602
|
} else {
|
|
1372
1603
|
const slots = [];
|
|
@@ -1375,83 +1606,55 @@ function eciesSealedPoeWrap(args) {
|
|
|
1375
1606
|
wrapSlotMlkem768X25519({
|
|
1376
1607
|
pubR: recipientPublicKeys[i],
|
|
1377
1608
|
eseed: args.eseeds ? args.eseeds[i] : void 0,
|
|
1378
|
-
cek
|
|
1609
|
+
cek,
|
|
1610
|
+
nonce
|
|
1379
1611
|
})
|
|
1380
1612
|
);
|
|
1381
1613
|
}
|
|
1382
1614
|
if (args.skipShuffle !== true) {
|
|
1383
1615
|
csprngShuffle(slots);
|
|
1384
1616
|
}
|
|
1385
|
-
slotsHash = computeSlotsHash({
|
|
1617
|
+
const slotsHash = computeSlotsHash({
|
|
1618
|
+
aead: SEALED_POE_AEAD,
|
|
1619
|
+
kem: "mlkem768x25519",
|
|
1620
|
+
nonce,
|
|
1621
|
+
slots,
|
|
1622
|
+
hashesHash
|
|
1623
|
+
});
|
|
1386
1624
|
envelope = {
|
|
1387
1625
|
scheme: 1,
|
|
1388
|
-
aead:
|
|
1626
|
+
aead: SEALED_POE_AEAD,
|
|
1389
1627
|
kem: "mlkem768x25519",
|
|
1390
1628
|
nonce,
|
|
1391
1629
|
slots,
|
|
1392
|
-
slots_mac:
|
|
1630
|
+
slots_mac: sizedSlotsMac(cek, slotsHash)
|
|
1393
1631
|
};
|
|
1394
1632
|
}
|
|
1395
|
-
const
|
|
1396
|
-
|
|
1397
|
-
kem: envelope.kem,
|
|
1398
|
-
nonce,
|
|
1399
|
-
slotsHash,
|
|
1400
|
-
slotsMac: envelope.slots_mac
|
|
1401
|
-
});
|
|
1402
|
-
const ciphertext = xchacha20Poly1305Encrypt({
|
|
1403
|
-
key: payloadKey,
|
|
1404
|
-
nonce,
|
|
1405
|
-
aad: adContent,
|
|
1633
|
+
const ciphertext = streamSeal({
|
|
1634
|
+
payloadKey: slotsPayloadKey({ cek, nonce }),
|
|
1406
1635
|
plaintext
|
|
1407
1636
|
});
|
|
1408
1637
|
return { envelope, ciphertext };
|
|
1409
1638
|
}
|
|
1410
|
-
function
|
|
1411
|
-
const
|
|
1412
|
-
ikm: cek,
|
|
1413
|
-
salt: EMPTY_SALT,
|
|
1414
|
-
info: CARDANO_POE_HKDF_INFO_SLOTS_MAC,
|
|
1415
|
-
length: 32
|
|
1416
|
-
});
|
|
1417
|
-
const slotsMac = hmac(sha256$1, hmacKey, slotsHash);
|
|
1639
|
+
function sizedSlotsMac(cek, slotsHash) {
|
|
1640
|
+
const slotsMac = computeSlotsMac({ cek, slotsHash });
|
|
1418
1641
|
if (slotsMac.length !== SLOTS_MAC_LENGTH) {
|
|
1419
1642
|
throw new Error(`internal: slots_mac.length=${slotsMac.length}, expected ${SLOTS_MAC_LENGTH}`);
|
|
1420
1643
|
}
|
|
1421
1644
|
return slotsMac;
|
|
1422
1645
|
}
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
var CHUNK_MAX_BYTES2 = 64;
|
|
1426
|
-
var UTF8_ENCODER2 = new TextEncoder();
|
|
1427
|
-
function chunkBytes(value) {
|
|
1428
|
-
if (value.length === 0) return [new Uint8Array(0)];
|
|
1429
|
-
const chunks = [];
|
|
1430
|
-
for (let i = 0; i < value.length; i += CHUNK_MAX_BYTES2) {
|
|
1431
|
-
chunks.push(value.subarray(i, Math.min(i + CHUNK_MAX_BYTES2, value.length)));
|
|
1432
|
-
}
|
|
1433
|
-
return chunks;
|
|
1434
|
-
}
|
|
1435
|
-
function chunkUri(uri) {
|
|
1436
|
-
const bytes = UTF8_ENCODER2.encode(uri);
|
|
1437
|
-
if (bytes.length === 0) return [""];
|
|
1438
|
-
if (bytes.length <= CHUNK_MAX_BYTES2) return [uri];
|
|
1439
|
-
const decoder = new TextDecoder("utf-8", { fatal: true });
|
|
1440
|
-
const chunks = [];
|
|
1441
|
-
let cursor = 0;
|
|
1442
|
-
while (cursor < bytes.length) {
|
|
1443
|
-
let end = Math.min(cursor + CHUNK_MAX_BYTES2, bytes.length);
|
|
1444
|
-
while (end < bytes.length && (bytes[end] & 192) === 128) end--;
|
|
1445
|
-
chunks.push(decoder.decode(bytes.subarray(cursor, end)));
|
|
1446
|
-
cursor = end;
|
|
1447
|
-
}
|
|
1448
|
-
return chunks;
|
|
1449
|
-
}
|
|
1646
|
+
new TextEncoder();
|
|
1647
|
+
new TextEncoder();
|
|
1450
1648
|
|
|
1451
1649
|
// src/client/off-host-sign.ts
|
|
1452
1650
|
var EMPTY_BYTES2 = new Uint8Array(0);
|
|
1453
1651
|
var ED25519_PUBLIC_KEY_LENGTH = 32;
|
|
1454
1652
|
var ED25519_SIGNATURE_LENGTH = 64;
|
|
1653
|
+
function cloneToOwnedBuffer(src) {
|
|
1654
|
+
const out = new Uint8Array(new ArrayBuffer(src.length));
|
|
1655
|
+
out.set(src);
|
|
1656
|
+
return out;
|
|
1657
|
+
}
|
|
1455
1658
|
var OffHostSignError = class extends Error {
|
|
1456
1659
|
code;
|
|
1457
1660
|
constructor(code, message) {
|
|
@@ -1510,8 +1713,7 @@ function assembleCoseSign1(args) {
|
|
|
1510
1713
|
payload: null,
|
|
1511
1714
|
signature: args.signature
|
|
1512
1715
|
});
|
|
1513
|
-
const
|
|
1514
|
-
const sigEntry = { cose_sign1: chunks };
|
|
1716
|
+
const sigEntry = { cose_sign1: cloneToOwnedBuffer(coseSign1Bytes) };
|
|
1515
1717
|
return { coseSign1Bytes, sigEntry };
|
|
1516
1718
|
}
|
|
1517
1719
|
function prepareSigStructureHashed(args) {
|
|
@@ -1553,8 +1755,7 @@ function assembleCoseSign1Hashed(args) {
|
|
|
1553
1755
|
payload: null,
|
|
1554
1756
|
signature: args.signature
|
|
1555
1757
|
});
|
|
1556
|
-
const
|
|
1557
|
-
const sigEntry = { cose_sign1: chunks };
|
|
1758
|
+
const sigEntry = { cose_sign1: cloneToOwnedBuffer(coseSign1Bytes) };
|
|
1558
1759
|
return { coseSign1Bytes, sigEntry };
|
|
1559
1760
|
}
|
|
1560
1761
|
|
|
@@ -1881,7 +2082,16 @@ function parseHttpError(args) {
|
|
|
1881
2082
|
return new ForbiddenError(init);
|
|
1882
2083
|
case "insufficient-scope":
|
|
1883
2084
|
return new InsufficientScopeError(init);
|
|
2085
|
+
// The three 402 funding/affordability failures are one condition to a
|
|
2086
|
+
// caller: the account cannot fund the operation. `insufficient-funds` is the
|
|
2087
|
+
// balance shortfall; `insufficient-storage-credit` is the storage funding
|
|
2088
|
+
// source being out of credit; `no-funding-grant` is the absence of any
|
|
2089
|
+
// funding source entitling the account beyond the free window. All three
|
|
2090
|
+
// surface as the same funding error so a caller routes the user to top up
|
|
2091
|
+
// without branching on the code.
|
|
1884
2092
|
case "insufficient-funds":
|
|
2093
|
+
case "insufficient-storage-credit":
|
|
2094
|
+
case "no-funding-grant":
|
|
1885
2095
|
return new InsufficientFundsError(init);
|
|
1886
2096
|
case "quote-expired":
|
|
1887
2097
|
return new QuoteExpiredError(init);
|
|
@@ -1993,6 +2203,25 @@ var InvalidClientConfigError = class extends Error {
|
|
|
1993
2203
|
function bytesToHex(bytes) {
|
|
1994
2204
|
return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
1995
2205
|
}
|
|
2206
|
+
|
|
2207
|
+
// src/client/partial-upload-error.ts
|
|
2208
|
+
var PartialUploadError = class extends Error {
|
|
2209
|
+
response;
|
|
2210
|
+
failed;
|
|
2211
|
+
constructor(response) {
|
|
2212
|
+
const failed = response.uploads.filter((u) => u.ok === false);
|
|
2213
|
+
super(
|
|
2214
|
+
`${failed.length} of ${response.uploads.length} upload(s) failed: ${failed.map((f) => `[${f.idx}] ${f.error.code} \u2014 ${f.error.detail}`).join("; ")}`
|
|
2215
|
+
);
|
|
2216
|
+
this.name = "PartialUploadError";
|
|
2217
|
+
this.response = response;
|
|
2218
|
+
this.failed = failed;
|
|
2219
|
+
}
|
|
2220
|
+
/** Convenience: the `idx` of every failed entry, in input order. */
|
|
2221
|
+
get failedIndices() {
|
|
2222
|
+
return this.failed.map((f) => f.idx);
|
|
2223
|
+
}
|
|
2224
|
+
};
|
|
1996
2225
|
function encodeCanonicalCbor4(value) {
|
|
1997
2226
|
return encode(value, {
|
|
1998
2227
|
cde: true,
|
|
@@ -2055,24 +2284,493 @@ function encodeLeavesList(args) {
|
|
|
2055
2284
|
return encodeCanonicalCbor4(map);
|
|
2056
2285
|
}
|
|
2057
2286
|
|
|
2058
|
-
// src/client/
|
|
2059
|
-
var
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2287
|
+
// src/client/resumable-source.ts
|
|
2288
|
+
var openHandlePromise;
|
|
2289
|
+
async function loadOpen() {
|
|
2290
|
+
if (openHandlePromise === void 0) {
|
|
2291
|
+
openHandlePromise = import('fs/promises').then((fs) => fs.open);
|
|
2292
|
+
}
|
|
2293
|
+
return openHandlePromise;
|
|
2294
|
+
}
|
|
2295
|
+
var HASH_STREAM_CHUNK_BYTES = 1024 * 1024;
|
|
2296
|
+
function isResumableSource(value) {
|
|
2297
|
+
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
|
|
2298
|
+
// here so the adapter contract is unambiguous regardless of check order. A
|
|
2299
|
+
// Blob is handled by its own branch, which adapts `.slice`/`.stream` to bytes.
|
|
2300
|
+
typeof value.arrayBuffer !== "function";
|
|
2301
|
+
}
|
|
2302
|
+
function isBlobLike(value) {
|
|
2303
|
+
return typeof value === "object" && value !== null && typeof value.size === "number" && typeof value.slice === "function" && typeof value.arrayBuffer === "function";
|
|
2304
|
+
}
|
|
2305
|
+
async function blobSlice(blob, start, end) {
|
|
2306
|
+
return new Uint8Array(await blob.slice(start, end).arrayBuffer());
|
|
2307
|
+
}
|
|
2308
|
+
async function* blobStream(blob) {
|
|
2309
|
+
const reader = blob.stream().getReader();
|
|
2310
|
+
try {
|
|
2311
|
+
for (; ; ) {
|
|
2312
|
+
const { done, value } = await reader.read();
|
|
2313
|
+
if (done) break;
|
|
2314
|
+
if (value) yield value;
|
|
2315
|
+
}
|
|
2316
|
+
} finally {
|
|
2317
|
+
reader.releaseLock();
|
|
2318
|
+
}
|
|
2319
|
+
}
|
|
2320
|
+
function fromBlob(blob) {
|
|
2321
|
+
return {
|
|
2322
|
+
size: blob.size,
|
|
2323
|
+
slice: (start, end) => blobSlice(blob, start, end),
|
|
2324
|
+
stream: () => blobStream(blob)
|
|
2325
|
+
};
|
|
2326
|
+
}
|
|
2327
|
+
function fromBytes(bytes) {
|
|
2328
|
+
return {
|
|
2329
|
+
size: bytes.byteLength,
|
|
2330
|
+
slice: (start, end) => bytes.subarray(start, end),
|
|
2331
|
+
stream: async function* () {
|
|
2332
|
+
for (let offset = 0; offset < bytes.byteLength; offset += HASH_STREAM_CHUNK_BYTES) {
|
|
2333
|
+
yield bytes.subarray(offset, Math.min(offset + HASH_STREAM_CHUNK_BYTES, bytes.byteLength));
|
|
2334
|
+
}
|
|
2335
|
+
}
|
|
2336
|
+
};
|
|
2337
|
+
}
|
|
2338
|
+
async function fromPath(path) {
|
|
2339
|
+
const open = await loadOpen();
|
|
2340
|
+
const sliceAt = async (start, end) => {
|
|
2341
|
+
const length = end - start;
|
|
2342
|
+
if (length <= 0) return new Uint8Array(0);
|
|
2343
|
+
const handle = await open(path, "r");
|
|
2344
|
+
try {
|
|
2345
|
+
const buffer = new Uint8Array(length);
|
|
2346
|
+
let filled = 0;
|
|
2347
|
+
while (filled < length) {
|
|
2348
|
+
const { bytesRead } = await handle.read(buffer, filled, length - filled, start + filled);
|
|
2349
|
+
if (bytesRead === 0) break;
|
|
2350
|
+
filled += bytesRead;
|
|
2351
|
+
}
|
|
2352
|
+
return filled === length ? buffer : buffer.subarray(0, filled);
|
|
2353
|
+
} finally {
|
|
2354
|
+
await handle.close();
|
|
2355
|
+
}
|
|
2356
|
+
};
|
|
2357
|
+
const streamFile = async function* () {
|
|
2358
|
+
const handle = await open(path, "r");
|
|
2359
|
+
try {
|
|
2360
|
+
const buffer = new Uint8Array(HASH_STREAM_CHUNK_BYTES);
|
|
2361
|
+
let position = 0;
|
|
2362
|
+
for (; ; ) {
|
|
2363
|
+
const { bytesRead } = await handle.read(buffer, 0, buffer.length, position);
|
|
2364
|
+
if (bytesRead === 0) break;
|
|
2365
|
+
position += bytesRead;
|
|
2366
|
+
yield buffer.slice(0, bytesRead);
|
|
2367
|
+
}
|
|
2368
|
+
} finally {
|
|
2369
|
+
await handle.close();
|
|
2370
|
+
}
|
|
2371
|
+
};
|
|
2372
|
+
const { size } = await statSize(path);
|
|
2373
|
+
return { size, slice: sliceAt, stream: streamFile };
|
|
2374
|
+
}
|
|
2375
|
+
async function statSize(path) {
|
|
2376
|
+
const open = await loadOpen();
|
|
2377
|
+
const handle = await open(path, "r");
|
|
2378
|
+
try {
|
|
2379
|
+
const stats = await handle.stat();
|
|
2380
|
+
return { size: stats.size };
|
|
2381
|
+
} finally {
|
|
2382
|
+
await handle.close();
|
|
2383
|
+
}
|
|
2384
|
+
}
|
|
2385
|
+
async function toResumableSource(input) {
|
|
2386
|
+
if (typeof input === "string") return fromPath(input);
|
|
2387
|
+
if (input instanceof Uint8Array) return fromBytes(input);
|
|
2388
|
+
if (isBlobLike(input)) return fromBlob(input);
|
|
2389
|
+
if (isResumableSource(input)) return input;
|
|
2390
|
+
throw new TypeError(
|
|
2391
|
+
"uploadResumable: unsupported source. Pass a Blob/File, a Uint8Array, a filesystem path string, or a ResumableSource."
|
|
2392
|
+
);
|
|
2393
|
+
}
|
|
2394
|
+
|
|
2395
|
+
// src/client/resumable-upload.ts
|
|
2396
|
+
var DEFAULT_RESUMABLE_THRESHOLD_BYTES = 50331648;
|
|
2397
|
+
var DEFAULT_RESUMABLE_CHUNK_BYTES = 50331648;
|
|
2398
|
+
var DEFAULT_PARALLELISM = 4;
|
|
2399
|
+
var DEFAULT_MAX_CHUNK_RETRIES = 4;
|
|
2400
|
+
var DEFAULT_CONTENT_TYPE = "application/octet-stream";
|
|
2401
|
+
var DEFAULT_TARGET = "arweave";
|
|
2402
|
+
var ATTEMPT_POLL_INTERVAL_MS = 1e3;
|
|
2403
|
+
var ATTEMPT_POLL_MAX_ATTEMPTS = 600;
|
|
2404
|
+
function bytesToHex2(bytes) {
|
|
2405
|
+
return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
2406
|
+
}
|
|
2407
|
+
var B64_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
2408
|
+
function bytesToBase64(bytes) {
|
|
2409
|
+
let out = "";
|
|
2410
|
+
let i = 0;
|
|
2411
|
+
for (; i + 2 < bytes.length; i += 3) {
|
|
2412
|
+
const n = bytes[i] << 16 | bytes[i + 1] << 8 | bytes[i + 2];
|
|
2413
|
+
out += B64_ALPHABET[n >> 18 & 63] + B64_ALPHABET[n >> 12 & 63] + B64_ALPHABET[n >> 6 & 63] + B64_ALPHABET[n & 63];
|
|
2414
|
+
}
|
|
2415
|
+
const rem = bytes.length - i;
|
|
2416
|
+
if (rem === 1) {
|
|
2417
|
+
const n = bytes[i] << 16;
|
|
2418
|
+
out += B64_ALPHABET[n >> 18 & 63] + B64_ALPHABET[n >> 12 & 63] + "==";
|
|
2419
|
+
} else if (rem === 2) {
|
|
2420
|
+
const n = bytes[i] << 16 | bytes[i + 1] << 8;
|
|
2421
|
+
out += B64_ALPHABET[n >> 18 & 63] + B64_ALPHABET[n >> 12 & 63] + B64_ALPHABET[n >> 6 & 63] + "=";
|
|
2422
|
+
}
|
|
2423
|
+
return out;
|
|
2424
|
+
}
|
|
2425
|
+
function jsonHeaders(config, idempotencyKey) {
|
|
2426
|
+
const headers = new Headers({ "content-type": "application/json", accept: "application/json" });
|
|
2427
|
+
if (config.apiKey !== void 0) headers.set("authorization", `Bearer ${config.apiKey}`);
|
|
2428
|
+
if (idempotencyKey !== void 0) headers.set("idempotency-key", idempotencyKey);
|
|
2429
|
+
return headers;
|
|
2430
|
+
}
|
|
2431
|
+
function octetHeaders(config, length, digestBase64) {
|
|
2432
|
+
const headers = new Headers({
|
|
2433
|
+
"content-type": "application/octet-stream",
|
|
2434
|
+
accept: "application/json",
|
|
2435
|
+
"content-length": String(length),
|
|
2436
|
+
digest: `sha-256=${digestBase64}`
|
|
2437
|
+
});
|
|
2438
|
+
if (config.apiKey !== void 0) headers.set("authorization", `Bearer ${config.apiKey}`);
|
|
2439
|
+
return headers;
|
|
2440
|
+
}
|
|
2441
|
+
var SESSIONS_PATH = "/api/v1/poe/uploads/sessions";
|
|
2442
|
+
function chunkRange(index, chunkBytes, totalBytes) {
|
|
2443
|
+
const start = index * chunkBytes;
|
|
2444
|
+
return [start, Math.min(start + chunkBytes, totalBytes)];
|
|
2445
|
+
}
|
|
2446
|
+
function missingIndices(received, chunkCount) {
|
|
2447
|
+
const have = new Set(received);
|
|
2448
|
+
const out = [];
|
|
2449
|
+
for (let i = 0; i < chunkCount; i++) if (!have.has(i)) out.push(i);
|
|
2450
|
+
return out;
|
|
2451
|
+
}
|
|
2452
|
+
function serverMissing(status) {
|
|
2453
|
+
if (Array.isArray(status.missing)) return status.missing;
|
|
2454
|
+
return missingIndices(status.received, status.chunk_count);
|
|
2455
|
+
}
|
|
2456
|
+
async function hashWholeFile(source) {
|
|
2457
|
+
return bytesToHex2(await sha256Stream(source.stream()));
|
|
2458
|
+
}
|
|
2459
|
+
async function createSession(config, body, signal) {
|
|
2460
|
+
const response = await config.fetch(`${config.baseUrl}${SESSIONS_PATH}`, {
|
|
2461
|
+
method: "POST",
|
|
2462
|
+
headers: jsonHeaders(config),
|
|
2463
|
+
body: JSON.stringify(body),
|
|
2464
|
+
...signal ? { signal } : {}
|
|
2465
|
+
});
|
|
2466
|
+
await throwIfNotOk(response);
|
|
2467
|
+
return await readJson(response);
|
|
2468
|
+
}
|
|
2469
|
+
async function getSessionStatus(config, sessionId, signal) {
|
|
2470
|
+
const response = await config.fetch(
|
|
2471
|
+
`${config.baseUrl}${SESSIONS_PATH}/${encodeURIComponent(sessionId)}`,
|
|
2472
|
+
{
|
|
2473
|
+
method: "GET",
|
|
2474
|
+
headers: jsonHeaders(config),
|
|
2475
|
+
...signal ? { signal } : {}
|
|
2476
|
+
}
|
|
2477
|
+
);
|
|
2478
|
+
await throwIfNotOk(response);
|
|
2479
|
+
return await readJson(response);
|
|
2480
|
+
}
|
|
2481
|
+
async function putChunk(config, sessionId, index, bytes, signal) {
|
|
2482
|
+
const digest = bytesToBase64(sha256(bytes));
|
|
2483
|
+
const response = await config.fetch(
|
|
2484
|
+
`${config.baseUrl}${SESSIONS_PATH}/${encodeURIComponent(sessionId)}/chunks/${index}`,
|
|
2485
|
+
{
|
|
2486
|
+
method: "PUT",
|
|
2487
|
+
headers: octetHeaders(config, bytes.byteLength, digest),
|
|
2488
|
+
// A Blob body streams without copying; a matching-digest re-PUT is an
|
|
2489
|
+
// idempotent 200 server-side, so a retried chunk is always safe.
|
|
2490
|
+
body: new Blob([bytes], { type: "application/octet-stream" }),
|
|
2491
|
+
...signal ? { signal } : {}
|
|
2492
|
+
}
|
|
2493
|
+
);
|
|
2494
|
+
await throwIfNotOk(response);
|
|
2495
|
+
return await readJson(response);
|
|
2496
|
+
}
|
|
2497
|
+
async function completeSession(config, sessionId, idempotencyKey, signal) {
|
|
2498
|
+
const response = await config.fetch(
|
|
2499
|
+
`${config.baseUrl}${SESSIONS_PATH}/${encodeURIComponent(sessionId)}/complete`,
|
|
2500
|
+
{
|
|
2501
|
+
method: "POST",
|
|
2502
|
+
headers: jsonHeaders(config, idempotencyKey),
|
|
2503
|
+
...signal ? { signal } : {}
|
|
2504
|
+
}
|
|
2505
|
+
);
|
|
2506
|
+
await throwIfNotOk(response);
|
|
2507
|
+
return await readJson(response);
|
|
2508
|
+
}
|
|
2509
|
+
async function pollAttempt(config, attemptId, signal) {
|
|
2510
|
+
for (let attempt = 0; attempt < ATTEMPT_POLL_MAX_ATTEMPTS; attempt++) {
|
|
2511
|
+
const response = await config.fetch(
|
|
2512
|
+
`${config.baseUrl}/api/v1/poe/uploads/attempts/${encodeURIComponent(attemptId)}`,
|
|
2513
|
+
{
|
|
2514
|
+
method: "GET",
|
|
2515
|
+
headers: jsonHeaders(config),
|
|
2516
|
+
...signal ? { signal } : {}
|
|
2517
|
+
}
|
|
2066
2518
|
);
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2519
|
+
await throwIfNotOk(response);
|
|
2520
|
+
const status = await readJson(response);
|
|
2521
|
+
if (status.state !== "reserved") return status;
|
|
2522
|
+
await delay(ATTEMPT_POLL_INTERVAL_MS, signal);
|
|
2070
2523
|
}
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2524
|
+
throw new ResumableUploadError(
|
|
2525
|
+
"ATTEMPT_POLL_TIMEOUT",
|
|
2526
|
+
`upload attempt ${attemptId} did not reach a terminal state in time`
|
|
2527
|
+
);
|
|
2528
|
+
}
|
|
2529
|
+
function delay(ms, signal) {
|
|
2530
|
+
return new Promise((resolve, reject) => {
|
|
2531
|
+
if (signal?.aborted) {
|
|
2532
|
+
reject(signalReason(signal));
|
|
2533
|
+
return;
|
|
2534
|
+
}
|
|
2535
|
+
const timer = setTimeout(() => {
|
|
2536
|
+
signal?.removeEventListener("abort", onAbort);
|
|
2537
|
+
resolve();
|
|
2538
|
+
}, ms);
|
|
2539
|
+
const onAbort = () => {
|
|
2540
|
+
clearTimeout(timer);
|
|
2541
|
+
reject(signalReason(signal));
|
|
2542
|
+
};
|
|
2543
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
2544
|
+
});
|
|
2545
|
+
}
|
|
2546
|
+
function signalReason(signal) {
|
|
2547
|
+
const reason = signal?.reason;
|
|
2548
|
+
return reason instanceof Error ? reason : new ResumableUploadError("ABORTED", "upload aborted");
|
|
2549
|
+
}
|
|
2550
|
+
var ResumableUploadError = class extends Error {
|
|
2551
|
+
code;
|
|
2552
|
+
constructor(code, message) {
|
|
2553
|
+
super(message);
|
|
2554
|
+
this.name = "ResumableUploadError";
|
|
2555
|
+
this.code = code;
|
|
2074
2556
|
}
|
|
2075
2557
|
};
|
|
2558
|
+
async function uploadChunks(config, sessionId, source, chunkBytes, totalBytes, missing, parallelism, maxRetries, signal) {
|
|
2559
|
+
let cursor = 0;
|
|
2560
|
+
const workers = [];
|
|
2561
|
+
const lanes = Math.max(1, Math.min(parallelism, missing.length || 1));
|
|
2562
|
+
for (let lane = 0; lane < lanes; lane++) {
|
|
2563
|
+
workers.push(
|
|
2564
|
+
(async () => {
|
|
2565
|
+
for (; ; ) {
|
|
2566
|
+
if (signal?.aborted) throw signalReason(signal);
|
|
2567
|
+
const next = cursor++;
|
|
2568
|
+
if (next >= missing.length) return;
|
|
2569
|
+
const index = missing[next];
|
|
2570
|
+
const [start, end] = chunkRange(index, chunkBytes, totalBytes);
|
|
2571
|
+
const bytes = await source.slice(start, end);
|
|
2572
|
+
await putChunkWithRetry(config, sessionId, index, bytes, maxRetries, signal);
|
|
2573
|
+
}
|
|
2574
|
+
})()
|
|
2575
|
+
);
|
|
2576
|
+
}
|
|
2577
|
+
await Promise.all(workers);
|
|
2578
|
+
}
|
|
2579
|
+
async function putChunkWithRetry(config, sessionId, index, bytes, maxRetries, signal) {
|
|
2580
|
+
let lastError;
|
|
2581
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
2582
|
+
if (signal?.aborted) throw signalReason(signal);
|
|
2583
|
+
try {
|
|
2584
|
+
await putChunk(config, sessionId, index, bytes, signal);
|
|
2585
|
+
return;
|
|
2586
|
+
} catch (err) {
|
|
2587
|
+
if (signal?.aborted) throw signalReason(signal);
|
|
2588
|
+
if (isTerminalChunkError(err)) throw err;
|
|
2589
|
+
lastError = err;
|
|
2590
|
+
if (attempt < maxRetries) {
|
|
2591
|
+
await delay(Math.min(250 * 2 ** attempt, 8e3), signal);
|
|
2592
|
+
}
|
|
2593
|
+
}
|
|
2594
|
+
}
|
|
2595
|
+
throw new ResumableUploadError(
|
|
2596
|
+
"CHUNK_UPLOAD_FAILED",
|
|
2597
|
+
`chunk ${index} failed after ${maxRetries + 1} attempt(s): ${lastError instanceof Error ? lastError.message : String(lastError)}`
|
|
2598
|
+
);
|
|
2599
|
+
}
|
|
2600
|
+
async function uploadResumable(config, singleShot, input) {
|
|
2601
|
+
const source = await toResumableSource(input.source);
|
|
2602
|
+
const target = input.target ?? DEFAULT_TARGET;
|
|
2603
|
+
const threshold = input.threshold ?? DEFAULT_RESUMABLE_THRESHOLD_BYTES;
|
|
2604
|
+
const totalBytes = source.size;
|
|
2605
|
+
if (totalBytes <= threshold && input.sessionId === void 0) {
|
|
2606
|
+
const bytes = await source.slice(0, totalBytes);
|
|
2607
|
+
const result = await singleShot({
|
|
2608
|
+
target,
|
|
2609
|
+
bytes,
|
|
2610
|
+
...input.idempotencyKey !== void 0 ? { idempotencyKey: input.idempotencyKey } : {},
|
|
2611
|
+
...input.signal ? { signal: input.signal } : {}
|
|
2612
|
+
});
|
|
2613
|
+
return {
|
|
2614
|
+
uri: result.uri,
|
|
2615
|
+
sha256: result.sha256,
|
|
2616
|
+
bytes: result.bytes,
|
|
2617
|
+
deduplicated: false,
|
|
2618
|
+
mode: "single-shot"
|
|
2619
|
+
};
|
|
2620
|
+
}
|
|
2621
|
+
return runSession(config, source, target, totalBytes, input);
|
|
2622
|
+
}
|
|
2623
|
+
async function runSession(config, source, target, totalBytes, input) {
|
|
2624
|
+
const signal = input.signal;
|
|
2625
|
+
let sessionId;
|
|
2626
|
+
let chunkBytes;
|
|
2627
|
+
let missing;
|
|
2628
|
+
let declaredSha256;
|
|
2629
|
+
let gridTotalBytes;
|
|
2630
|
+
if (input.sessionId !== void 0) {
|
|
2631
|
+
const status = await getSessionStatus(config, input.sessionId, signal);
|
|
2632
|
+
if (status.state === "completed" && status.uri !== null) {
|
|
2633
|
+
return {
|
|
2634
|
+
uri: status.uri,
|
|
2635
|
+
sha256: status.sha256,
|
|
2636
|
+
bytes: status.total_bytes,
|
|
2637
|
+
deduplicated: false,
|
|
2638
|
+
mode: "chunked"
|
|
2639
|
+
};
|
|
2640
|
+
}
|
|
2641
|
+
if (status.state === "failed" || status.state === "expired") {
|
|
2642
|
+
throw new ResumableUploadError(
|
|
2643
|
+
"SESSION_FAILED",
|
|
2644
|
+
`cannot resume session ${input.sessionId} in state '${status.state}'`
|
|
2645
|
+
);
|
|
2646
|
+
}
|
|
2647
|
+
sessionId = status.session_id;
|
|
2648
|
+
declaredSha256 = status.sha256;
|
|
2649
|
+
chunkBytes = status.chunk_bytes;
|
|
2650
|
+
gridTotalBytes = status.total_bytes;
|
|
2651
|
+
missing = serverMissing(status);
|
|
2652
|
+
} else {
|
|
2653
|
+
declaredSha256 = await hashWholeFile(source);
|
|
2654
|
+
const requestedChunkBytes = input.chunkBytes ?? DEFAULT_RESUMABLE_CHUNK_BYTES;
|
|
2655
|
+
const created = await createSession(
|
|
2656
|
+
config,
|
|
2657
|
+
{
|
|
2658
|
+
target,
|
|
2659
|
+
sha256: declaredSha256,
|
|
2660
|
+
total_bytes: totalBytes,
|
|
2661
|
+
chunk_bytes: requestedChunkBytes,
|
|
2662
|
+
content_type: input.contentType ?? DEFAULT_CONTENT_TYPE
|
|
2663
|
+
},
|
|
2664
|
+
signal
|
|
2665
|
+
);
|
|
2666
|
+
if ("deduplicated" in created) {
|
|
2667
|
+
return {
|
|
2668
|
+
uri: created.uri,
|
|
2669
|
+
sha256: created.sha256,
|
|
2670
|
+
bytes: created.bytes,
|
|
2671
|
+
deduplicated: true,
|
|
2672
|
+
mode: "chunked"
|
|
2673
|
+
};
|
|
2674
|
+
}
|
|
2675
|
+
sessionId = created.session_id;
|
|
2676
|
+
chunkBytes = created.chunk_bytes;
|
|
2677
|
+
gridTotalBytes = totalBytes;
|
|
2678
|
+
missing = missingIndices(created.received, created.chunk_count);
|
|
2679
|
+
}
|
|
2680
|
+
if (missing.length > 0) {
|
|
2681
|
+
await uploadChunks(
|
|
2682
|
+
config,
|
|
2683
|
+
sessionId,
|
|
2684
|
+
source,
|
|
2685
|
+
chunkBytes,
|
|
2686
|
+
gridTotalBytes,
|
|
2687
|
+
missing,
|
|
2688
|
+
input.parallelism ?? DEFAULT_PARALLELISM,
|
|
2689
|
+
input.maxChunkRetries ?? DEFAULT_MAX_CHUNK_RETRIES,
|
|
2690
|
+
signal
|
|
2691
|
+
);
|
|
2692
|
+
}
|
|
2693
|
+
return finishSession(config, sessionId, declaredSha256, input);
|
|
2694
|
+
}
|
|
2695
|
+
async function finishSession(config, sessionId, declaredSha256, input) {
|
|
2696
|
+
const signal = input.signal;
|
|
2697
|
+
const idempotencyKey = input.idempotencyKey ?? `resumable-${declaredSha256}`;
|
|
2698
|
+
const COMPLETE_RETRIES = 2;
|
|
2699
|
+
for (let attempt = 0; attempt <= COMPLETE_RETRIES; attempt++) {
|
|
2700
|
+
try {
|
|
2701
|
+
const completion = await completeSession(config, sessionId, idempotencyKey, signal);
|
|
2702
|
+
if ("ok" in completion) {
|
|
2703
|
+
return {
|
|
2704
|
+
uri: completion.uri,
|
|
2705
|
+
sha256: completion.sha256,
|
|
2706
|
+
bytes: completion.bytes,
|
|
2707
|
+
// The server sends the number 0 for a dedup-on-commit (the bytes were
|
|
2708
|
+
// already stored, so nothing was charged); compare numerically.
|
|
2709
|
+
deduplicated: completion.charged_usd_micros === 0,
|
|
2710
|
+
mode: "chunked"
|
|
2711
|
+
};
|
|
2712
|
+
}
|
|
2713
|
+
return resolveAccepted(config, completion.attempt_id, signal);
|
|
2714
|
+
} catch (err) {
|
|
2715
|
+
if (attempt < COMPLETE_RETRIES && isIncompleteUpload(err)) {
|
|
2716
|
+
const status = await getSessionStatus(config, sessionId, signal);
|
|
2717
|
+
const stillMissing = serverMissing(status);
|
|
2718
|
+
if (stillMissing.length === 0) continue;
|
|
2719
|
+
await uploadChunks(
|
|
2720
|
+
config,
|
|
2721
|
+
sessionId,
|
|
2722
|
+
await toResumableSource(input.source),
|
|
2723
|
+
status.chunk_bytes,
|
|
2724
|
+
// Re-bound the resend grid against the server's declared total too, so a
|
|
2725
|
+
// source that grew during the upload cannot over-read the final chunk.
|
|
2726
|
+
status.total_bytes,
|
|
2727
|
+
stillMissing,
|
|
2728
|
+
input.parallelism ?? DEFAULT_PARALLELISM,
|
|
2729
|
+
input.maxChunkRetries ?? DEFAULT_MAX_CHUNK_RETRIES,
|
|
2730
|
+
signal
|
|
2731
|
+
);
|
|
2732
|
+
continue;
|
|
2733
|
+
}
|
|
2734
|
+
throw err;
|
|
2735
|
+
}
|
|
2736
|
+
}
|
|
2737
|
+
throw new ResumableUploadError(
|
|
2738
|
+
"SESSION_FAILED",
|
|
2739
|
+
`session ${sessionId} could not be completed after resending missing chunks`
|
|
2740
|
+
);
|
|
2741
|
+
}
|
|
2742
|
+
async function resolveAccepted(config, attemptId, signal) {
|
|
2743
|
+
const status = await pollAttempt(config, attemptId, signal);
|
|
2744
|
+
if (status.state === "released") {
|
|
2745
|
+
throw new ResumableUploadError(
|
|
2746
|
+
"ATTEMPT_FAILED",
|
|
2747
|
+
`upload attempt ${attemptId} was released: ${status.reason}`
|
|
2748
|
+
);
|
|
2749
|
+
}
|
|
2750
|
+
if (status.uri.length === 0) {
|
|
2751
|
+
throw new ResumableUploadError(
|
|
2752
|
+
"ATTEMPT_FAILED",
|
|
2753
|
+
`upload attempt ${attemptId} committed without a uri`
|
|
2754
|
+
);
|
|
2755
|
+
}
|
|
2756
|
+
return {
|
|
2757
|
+
uri: status.uri,
|
|
2758
|
+
sha256: status.sha256,
|
|
2759
|
+
bytes: status.bytes,
|
|
2760
|
+
// A committed attempt that charged nothing deduped against bytes already
|
|
2761
|
+
// stored for this account on this backend.
|
|
2762
|
+
deduplicated: status.charged_usd_micros === 0,
|
|
2763
|
+
mode: "chunked"
|
|
2764
|
+
};
|
|
2765
|
+
}
|
|
2766
|
+
function isTerminalChunkError(err) {
|
|
2767
|
+
if (!(err instanceof Label309HttpError)) return false;
|
|
2768
|
+
const status = err.httpStatus;
|
|
2769
|
+
return status >= 400 && status < 500 && status !== 408 && status !== 429;
|
|
2770
|
+
}
|
|
2771
|
+
function isIncompleteUpload(err) {
|
|
2772
|
+
return typeof err === "object" && err !== null && "code" in err && err.code === "incomplete-upload";
|
|
2773
|
+
}
|
|
2076
2774
|
|
|
2077
2775
|
// src/client/publish.ts
|
|
2078
2776
|
var ED25519_PUBLIC_KEY_LENGTH2 = 32;
|
|
@@ -2089,7 +2787,7 @@ var PublishError = class extends Error {
|
|
|
2089
2787
|
this.code = code;
|
|
2090
2788
|
}
|
|
2091
2789
|
};
|
|
2092
|
-
function
|
|
2790
|
+
function bytesToHex3(bytes) {
|
|
2093
2791
|
return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
2094
2792
|
}
|
|
2095
2793
|
function hexToBytes(hex) {
|
|
@@ -2110,14 +2808,14 @@ function toBytes(content) {
|
|
|
2110
2808
|
if (typeof content === "string") return new TextEncoder().encode(content);
|
|
2111
2809
|
return content;
|
|
2112
2810
|
}
|
|
2113
|
-
function
|
|
2811
|
+
function cloneToOwnedBuffer2(src) {
|
|
2114
2812
|
const out = new Uint8Array(new ArrayBuffer(src.length));
|
|
2115
2813
|
out.set(src);
|
|
2116
2814
|
return out;
|
|
2117
2815
|
}
|
|
2118
2816
|
function hashContent(bytes, alg) {
|
|
2119
|
-
if (alg === "sha2-256") return
|
|
2120
|
-
if (alg === "blake2b-256") return
|
|
2817
|
+
if (alg === "sha2-256") return cloneToOwnedBuffer2(sha256(bytes));
|
|
2818
|
+
if (alg === "blake2b-256") return cloneToOwnedBuffer2(blake2b256(bytes));
|
|
2121
2819
|
throw new PublishError(
|
|
2122
2820
|
"UNSUPPORTED_HASH_ALG",
|
|
2123
2821
|
`hashAlg must be 'sha2-256' or 'blake2b-256', got '${alg}'`
|
|
@@ -2201,29 +2899,45 @@ async function postPublish(config, recordBytesHex, quoteId, idempotencyKey) {
|
|
|
2201
2899
|
const parsed = await readJson2(response);
|
|
2202
2900
|
return { ...parsed, dedup_hit: response.status === 200 };
|
|
2203
2901
|
}
|
|
2204
|
-
|
|
2902
|
+
var singleShotUpload = (config) => async ({ target, bytes, idempotencyKey, signal }) => {
|
|
2205
2903
|
const form = new FormData();
|
|
2206
|
-
form.append("target",
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
`file_${idx}.bin`
|
|
2213
|
-
);
|
|
2214
|
-
}
|
|
2904
|
+
form.append("target", target);
|
|
2905
|
+
form.append(
|
|
2906
|
+
"file_0",
|
|
2907
|
+
new Blob([bytes], { type: "application/octet-stream" }),
|
|
2908
|
+
"file_0.bin"
|
|
2909
|
+
);
|
|
2215
2910
|
const response = await config.fetch(`${config.baseUrl}/api/v1/poe/uploads`, {
|
|
2216
2911
|
method: "POST",
|
|
2217
2912
|
headers: buildMultipartHeaders(config.apiKey, idempotencyKey),
|
|
2218
|
-
body: form
|
|
2913
|
+
body: form,
|
|
2914
|
+
...signal ? { signal } : {}
|
|
2219
2915
|
});
|
|
2220
2916
|
await throwIfNotOk2(response);
|
|
2221
2917
|
const result = await readJson2(response);
|
|
2222
|
-
const
|
|
2223
|
-
if (
|
|
2918
|
+
const entry = result.uploads[0];
|
|
2919
|
+
if (entry === void 0 || entry.ok === false) {
|
|
2224
2920
|
throw new PartialUploadError(result);
|
|
2225
2921
|
}
|
|
2226
|
-
|
|
2922
|
+
const ok = entry;
|
|
2923
|
+
return { uri: ok.uri, sha256: ok.sha256, bytes: ok.bytes };
|
|
2924
|
+
};
|
|
2925
|
+
async function uploadBlob(config, bytes, idempotencyKey) {
|
|
2926
|
+
const target = STORAGE_TARGET_ARWEAVE;
|
|
2927
|
+
if (bytes.byteLength <= DEFAULT_RESUMABLE_THRESHOLD_BYTES) {
|
|
2928
|
+
const single = await singleShotUpload(config)({
|
|
2929
|
+
target,
|
|
2930
|
+
bytes,
|
|
2931
|
+
...idempotencyKey !== void 0 ? { idempotencyKey } : {}
|
|
2932
|
+
});
|
|
2933
|
+
return single.uri;
|
|
2934
|
+
}
|
|
2935
|
+
const result = await uploadResumable(config, singleShotUpload(config), {
|
|
2936
|
+
target,
|
|
2937
|
+
source: bytes,
|
|
2938
|
+
...idempotencyKey !== void 0 ? { idempotencyKey } : {}
|
|
2939
|
+
});
|
|
2940
|
+
return result.uri;
|
|
2227
2941
|
}
|
|
2228
2942
|
async function publishContent(config, input) {
|
|
2229
2943
|
if (input.signer !== void 0) assertSigner(input.signer);
|
|
@@ -2235,7 +2949,7 @@ async function publishContent(config, input) {
|
|
|
2235
2949
|
items: [{ hashes: { [hashAlg]: digest } }]
|
|
2236
2950
|
};
|
|
2237
2951
|
const recordBytes = await encodeRecord(record, input.signer);
|
|
2238
|
-
return postPublish(config,
|
|
2952
|
+
return postPublish(config, bytesToHex3(recordBytes), input.quoteId, input.idempotencyKey);
|
|
2239
2953
|
}
|
|
2240
2954
|
var DIGEST_BYTE_LENGTH = {
|
|
2241
2955
|
"sha2-256": 32,
|
|
@@ -2267,14 +2981,14 @@ async function publishPrehashed(config, input) {
|
|
|
2267
2981
|
`hashes[${alg}] must be a ${expected}-byte digest (got ${bytes.length} bytes)`
|
|
2268
2982
|
);
|
|
2269
2983
|
}
|
|
2270
|
-
decoded[alg] =
|
|
2984
|
+
decoded[alg] = cloneToOwnedBuffer2(bytes);
|
|
2271
2985
|
}
|
|
2272
2986
|
const record = {
|
|
2273
2987
|
v: 1,
|
|
2274
2988
|
items: [{ hashes: decoded }]
|
|
2275
2989
|
};
|
|
2276
2990
|
const recordBytes = await encodeRecord(record, input.signer);
|
|
2277
|
-
return postPublish(config,
|
|
2991
|
+
return postPublish(config, bytesToHex3(recordBytes), input.quoteId, input.idempotencyKey);
|
|
2278
2992
|
}
|
|
2279
2993
|
async function publishSealed(config, input) {
|
|
2280
2994
|
if (input.signer !== void 0) assertSigner(input.signer);
|
|
@@ -2298,41 +3012,42 @@ async function publishSealed(config, input) {
|
|
|
2298
3012
|
const hashAlg = input.hashAlg ?? "sha2-256";
|
|
2299
3013
|
const plaintext = toBytes(input.content);
|
|
2300
3014
|
const plaintextDigest = hashContent(plaintext, hashAlg);
|
|
3015
|
+
const hashes2 = { [hashAlg]: plaintextDigest };
|
|
2301
3016
|
const sealed = eciesSealedPoeWrap({
|
|
2302
3017
|
plaintext,
|
|
3018
|
+
hashes: hashes2,
|
|
2303
3019
|
recipientPublicKeys: input.recipients.map((r) => r),
|
|
2304
3020
|
kem
|
|
2305
3021
|
});
|
|
2306
|
-
const
|
|
2307
|
-
const uri = uploadsResp.uploads[0].uri;
|
|
3022
|
+
const uri = await uploadBlob(config, sealed.ciphertext, input.idempotencyKey);
|
|
2308
3023
|
const env = sealed.envelope;
|
|
2309
3024
|
const slots = env.kem === "mlkem768x25519" ? env.slots.map((s) => ({
|
|
2310
|
-
kem_ct: s.kem_ct
|
|
2311
|
-
wrap:
|
|
3025
|
+
kem_ct: cloneToOwnedBuffer2(s.kem_ct),
|
|
3026
|
+
wrap: cloneToOwnedBuffer2(s.wrap)
|
|
2312
3027
|
})) : env.slots.map((s) => ({
|
|
2313
|
-
epk:
|
|
2314
|
-
wrap:
|
|
3028
|
+
epk: cloneToOwnedBuffer2(s.epk),
|
|
3029
|
+
wrap: cloneToOwnedBuffer2(s.wrap)
|
|
2315
3030
|
}));
|
|
2316
3031
|
const envelope = {
|
|
2317
3032
|
scheme: 1,
|
|
2318
3033
|
aead: env.aead,
|
|
2319
3034
|
kem: env.kem,
|
|
2320
|
-
nonce:
|
|
3035
|
+
nonce: cloneToOwnedBuffer2(env.nonce),
|
|
2321
3036
|
slots,
|
|
2322
|
-
slots_mac:
|
|
3037
|
+
slots_mac: cloneToOwnedBuffer2(env.slots_mac)
|
|
2323
3038
|
};
|
|
2324
3039
|
const record = {
|
|
2325
3040
|
v: 1,
|
|
2326
3041
|
items: [
|
|
2327
3042
|
{
|
|
2328
|
-
hashes:
|
|
2329
|
-
uris: [
|
|
3043
|
+
hashes: hashes2,
|
|
3044
|
+
uris: [uri],
|
|
2330
3045
|
enc: envelope
|
|
2331
3046
|
}
|
|
2332
3047
|
]
|
|
2333
3048
|
};
|
|
2334
3049
|
const recordBytes = await encodeRecord(record, input.signer);
|
|
2335
|
-
return postPublish(config,
|
|
3050
|
+
return postPublish(config, bytesToHex3(recordBytes), input.quoteId, input.idempotencyKey);
|
|
2336
3051
|
}
|
|
2337
3052
|
async function publishMerkle(config, input) {
|
|
2338
3053
|
if (input.signer !== void 0) assertSigner(input.signer);
|
|
@@ -2355,21 +3070,20 @@ async function publishMerkle(config, input) {
|
|
|
2355
3070
|
}
|
|
2356
3071
|
return bytes;
|
|
2357
3072
|
});
|
|
2358
|
-
const root =
|
|
3073
|
+
const root = cloneToOwnedBuffer2(merkleSha2256Root(leaves));
|
|
2359
3074
|
const leavesListCbor = encodeLeavesList({ leaves, root });
|
|
2360
|
-
const
|
|
2361
|
-
const uri = uploadsResp.uploads[0].uri;
|
|
3075
|
+
const uri = await uploadBlob(config, leavesListCbor, input.idempotencyKey);
|
|
2362
3076
|
const merkleEntry = {
|
|
2363
3077
|
alg: MERKLE_ALG_ID,
|
|
2364
3078
|
root,
|
|
2365
3079
|
leaf_count: leaves.length,
|
|
2366
|
-
uris: [
|
|
3080
|
+
uris: [uri]
|
|
2367
3081
|
};
|
|
2368
3082
|
const record = { v: 1, merkle: [merkleEntry] };
|
|
2369
3083
|
const recordBytes = await encodeRecord(record, input.signer);
|
|
2370
3084
|
const published = await postPublish(
|
|
2371
3085
|
config,
|
|
2372
|
-
|
|
3086
|
+
bytesToHex3(recordBytes),
|
|
2373
3087
|
input.quoteId,
|
|
2374
3088
|
input.idempotencyKey
|
|
2375
3089
|
);
|
|
@@ -2377,7 +3091,7 @@ async function publishMerkle(config, input) {
|
|
|
2377
3091
|
id: published.id,
|
|
2378
3092
|
tx_hash: published.tx_hash,
|
|
2379
3093
|
status: published.status,
|
|
2380
|
-
root:
|
|
3094
|
+
root: bytesToHex3(root),
|
|
2381
3095
|
leaf_count: leaves.length,
|
|
2382
3096
|
ar_uri: uri,
|
|
2383
3097
|
balance_after_usd_micros: published.balance_after_usd_micros
|
|
@@ -2472,6 +3186,65 @@ var PoeNamespace = class {
|
|
|
2472
3186
|
await throwIfNotOk(response);
|
|
2473
3187
|
return await readJson(response);
|
|
2474
3188
|
}
|
|
3189
|
+
/**
|
|
3190
|
+
* Upload a single file of any size, choosing the ingress path by size.
|
|
3191
|
+
*
|
|
3192
|
+
* A file at or below `threshold` (default ~48 MiB) is sent with the unchanged
|
|
3193
|
+
* single-shot `uploads()` multipart call. A larger file is uploaded as a
|
|
3194
|
+
* resumable, content-addressed session: the helper streams the whole-file
|
|
3195
|
+
* SHA-256 once (never buffering a multi-GB file), creates a session, PUTs each
|
|
3196
|
+
* chunk (several in parallel, retrying a failed chunk), then completes —
|
|
3197
|
+
* polling the shared attempt endpoint when completion is accepted
|
|
3198
|
+
* asynchronously. Both paths converge on one `ar://` URI.
|
|
3199
|
+
*
|
|
3200
|
+
* The chunk size is the server's authoritative `chunk_bytes` from the create
|
|
3201
|
+
* response, clamped to its `max_chunk_bytes` ceiling; the client's `chunkBytes`
|
|
3202
|
+
* is only a request. A create-time dedup hit returns the existing URI without
|
|
3203
|
+
* uploading; a `402` funding error is surfaced as a typed error.
|
|
3204
|
+
*
|
|
3205
|
+
* The `source` works in both runtimes: a `Blob`/`File` in the browser, a
|
|
3206
|
+
* `Uint8Array`, a filesystem path string, or a pre-adapted `ResumableSource`
|
|
3207
|
+
* on the server. To resume an interrupted upload, pass the prior `sessionId`;
|
|
3208
|
+
* the helper GETs its status and uploads only the missing chunks.
|
|
3209
|
+
*/
|
|
3210
|
+
async uploadResumable(input) {
|
|
3211
|
+
return uploadResumable(this.config, this.singleShotUpload, input);
|
|
3212
|
+
}
|
|
3213
|
+
/**
|
|
3214
|
+
* Upload exactly one blob via the single-shot multipart route and resolve its
|
|
3215
|
+
* `ar://` URI. Backs the small-file branch of `uploadResumable`; it shares the
|
|
3216
|
+
* `uploads()` wire shape but takes one blob and an optional abort signal, and
|
|
3217
|
+
* surfaces a per-file failure as a `PartialUploadError` (the resumable helper
|
|
3218
|
+
* promises a single resolved URI, unlike the raw `uploads()` passthrough).
|
|
3219
|
+
*/
|
|
3220
|
+
singleShotUpload = async ({
|
|
3221
|
+
target,
|
|
3222
|
+
bytes,
|
|
3223
|
+
idempotencyKey,
|
|
3224
|
+
signal
|
|
3225
|
+
}) => {
|
|
3226
|
+
const form = new FormData();
|
|
3227
|
+
form.append("target", target);
|
|
3228
|
+
form.append(
|
|
3229
|
+
"file_0",
|
|
3230
|
+
new Blob([bytes], { type: "application/octet-stream" }),
|
|
3231
|
+
"file_0.bin"
|
|
3232
|
+
);
|
|
3233
|
+
const response = await this.config.fetch(`${this.config.baseUrl}/api/v1/poe/uploads`, {
|
|
3234
|
+
method: "POST",
|
|
3235
|
+
headers: buildMultipartHeaders2({ apiKey: this.config.apiKey, idempotencyKey }),
|
|
3236
|
+
body: form,
|
|
3237
|
+
...signal ? { signal } : {}
|
|
3238
|
+
});
|
|
3239
|
+
await throwIfNotOk(response);
|
|
3240
|
+
const result = await readJson(response);
|
|
3241
|
+
const entry = result.uploads[0];
|
|
3242
|
+
if (entry === void 0 || entry.ok === false) {
|
|
3243
|
+
throw new PartialUploadError(result);
|
|
3244
|
+
}
|
|
3245
|
+
const ok = entry;
|
|
3246
|
+
return { uri: ok.uri, sha256: ok.sha256, bytes: ok.bytes };
|
|
3247
|
+
};
|
|
2475
3248
|
/**
|
|
2476
3249
|
* Submit a single finalised canonical-CBOR record to Cardano. Caller is
|
|
2477
3250
|
* responsible for constructing the record bytes (use `publishContent` /
|
|
@@ -2719,6 +3492,6 @@ var Label309Client = class {
|
|
|
2719
3492
|
(*! noble-post-quantum - MIT License (c) 2024 Paul Miller (paulmillr.com) *)
|
|
2720
3493
|
*/
|
|
2721
3494
|
|
|
2722
|
-
export { AccountNamespace, BatchEmptyError, BatchTooLargeError, ForbiddenError, IdempotencyConflictError, InsufficientFundsError, InsufficientScopeError, InternalServerError, InvalidBodyError, InvalidClientConfigError, Label309Client, Label309HttpError, MalformedCborError, NotFoundError, OffHostSignError, PartialUploadError, PoeNamespace, PublishError, QuoteAlreadyConsumedError, QuoteExpiredError, QuoteNotFoundError, RateLimitedError, RecordNotFoundError, RecordsNamespace, ServiceUnavailableError, UnauthorizedError, ValidationFailedError, assembleCoseSign1, assembleCoseSign1Hashed, buildToSign, parseHttpError, prepareSigStructure, prepareSigStructureHashed };
|
|
3495
|
+
export { AccountNamespace, BatchEmptyError, BatchTooLargeError, DEFAULT_RESUMABLE_CHUNK_BYTES, DEFAULT_RESUMABLE_THRESHOLD_BYTES, ForbiddenError, IdempotencyConflictError, InsufficientFundsError, InsufficientScopeError, InternalServerError, InvalidBodyError, InvalidClientConfigError, Label309Client, Label309HttpError, MalformedCborError, NotFoundError, OffHostSignError, PartialUploadError, PoeNamespace, PublishError, QuoteAlreadyConsumedError, QuoteExpiredError, QuoteNotFoundError, RateLimitedError, RecordNotFoundError, RecordsNamespace, ResumableUploadError, ServiceUnavailableError, UnauthorizedError, ValidationFailedError, assembleCoseSign1, assembleCoseSign1Hashed, buildToSign, parseHttpError, prepareSigStructure, prepareSigStructureHashed, toResumableSource };
|
|
2723
3496
|
//# sourceMappingURL=index.js.map
|
|
2724
3497
|
//# sourceMappingURL=index.js.map
|