@pinionengineering/prover-client 0.9.0 → 0.11.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/README.md +131 -54
- package/dist/challenge.d.ts +8 -0
- package/dist/challenge.d.ts.map +1 -1
- package/dist/challenge.js +10 -0
- package/dist/challenge.js.map +1 -1
- package/dist/client.d.ts +117 -49
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +187 -72
- package/dist/client.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +23 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# @
|
|
1
|
+
# @pinionengineering/prover-client
|
|
2
2
|
|
|
3
3
|
JavaScript / TypeScript client for **pinion-prover** storage-proof service.
|
|
4
4
|
|
|
@@ -9,22 +9,46 @@ Implements the SW-Pub challenger role:
|
|
|
9
9
|
- Sends challenges to the prover and receives proofs
|
|
10
10
|
- **Cryptographically verifies proofs** using BN254 Ate pairings
|
|
11
11
|
|
|
12
|
-
Most IPFS pinning clients trust HTTP 200 as proof of storage
|
|
12
|
+
Most IPFS pinning clients trust HTTP 200 as proof of storage, but a server
|
|
13
13
|
can return 200 without actually holding your data. This library closes that gap:
|
|
14
14
|
the server must solve a cryptographic challenge that is **only solvable by a party
|
|
15
15
|
who has the block data**, and you verify the answer with a BN254 pairing equation
|
|
16
16
|
in your browser or Node.js process.
|
|
17
17
|
|
|
18
|
+
Don't want to write JS? [The Go client in this same repo](../go#readme) documents a
|
|
19
|
+
no-code path: export a key + roots file from the
|
|
20
|
+
[dashboard](https://pinion.build), then run the same audit loop with the
|
|
21
|
+
`testclient` CLI. `testclient import` followed by `testclient audit --all --loop`
|
|
22
|
+
is the whole workflow, no account needed for the audit itself.
|
|
23
|
+
|
|
18
24
|
---
|
|
19
25
|
|
|
20
26
|
## Installation
|
|
21
27
|
|
|
22
28
|
```bash
|
|
23
|
-
npm install @
|
|
29
|
+
npm install @pinionengineering/prover-client
|
|
24
30
|
```
|
|
25
31
|
|
|
26
32
|
Requires Node.js ≥ 18. Works in modern browsers with native `crypto.getRandomValues` and `atob`.
|
|
27
33
|
|
|
34
|
+
### Upgrading from 0.10.x
|
|
35
|
+
|
|
36
|
+
`tag()` and `prove()` are now **submit-only**: they return a job handle
|
|
37
|
+
immediately (`{ jobId, ... }`) instead of blocking until the job finishes.
|
|
38
|
+
Await the new `waitForTag(jobId, options?)` / `waitForProve(jobId, options?)`
|
|
39
|
+
to wait for a terminal state, or poll `tagStatus(jobId)`/`proveStatus(jobId)`
|
|
40
|
+
yourself. There is **no default deadline anymore** — the old hardcoded 60s
|
|
41
|
+
(`prove`)/10min (`tag`) timeouts are gone, since a proof or tag job can
|
|
42
|
+
legitimately take longer and the server was never designed to respect an
|
|
43
|
+
arbitrary client-side ceiling. If you want the old ceiling back, it's a
|
|
44
|
+
strict superset away: pass `signal: AbortSignal.timeout(ms)` to
|
|
45
|
+
`waitForProve()`/`waitForTag()`/`audit()`.
|
|
46
|
+
|
|
47
|
+
`audit(keyId, setup)` is unchanged in shape — still one call that submits,
|
|
48
|
+
waits, and verifies — but now also accepts `pollIntervalMs`/`signal`/
|
|
49
|
+
`onStatus` so you can build progress UI or cancel a long-running round; see
|
|
50
|
+
its section below.
|
|
51
|
+
|
|
28
52
|
---
|
|
29
53
|
|
|
30
54
|
## How It Works
|
|
@@ -35,14 +59,17 @@ The protocol has two distinct phases:
|
|
|
35
59
|
|
|
36
60
|
1. Call `createKey()`. The server generates a BN254 key pair and stores the private
|
|
37
61
|
scalar α. You receive:
|
|
38
|
-
- `keyId
|
|
39
|
-
- `publicKey
|
|
62
|
+
- `keyId`: identifier for this key on the server
|
|
63
|
+
- `publicKey`: the public half: `V = α·G₂` (G2 point) and `U[0..s-1]` (G1 points).
|
|
40
64
|
These are the values needed to verify proofs; store them if you want to verify
|
|
41
65
|
independently of the server later.
|
|
42
66
|
|
|
43
|
-
2. Call `tag(cid, keyId)` for each pinned CID
|
|
44
|
-
|
|
45
|
-
|
|
67
|
+
2. Call `tag(cid, keyId)` for each pinned CID, then `waitForTag(jobId)`.
|
|
68
|
+
Tagging is asynchronous — `tag()` submits the job and returns a job
|
|
69
|
+
handle immediately; the server walks the IPFS DAG, computes per-block
|
|
70
|
+
authentication tags, and stores them in the background. `waitForTag()`
|
|
71
|
+
polls until it's done and returns the **block IDs** for that root: the
|
|
72
|
+
`CID.Bytes()` of every block in the DAG, in TagList order.
|
|
46
73
|
|
|
47
74
|
3. Call `getSetup(keyId)` to fetch the full setup document. The response contains:
|
|
48
75
|
- The public key (same as returned by `createKey()`)
|
|
@@ -57,7 +84,7 @@ Each audit round is independent and stateless with respect to prior rounds:
|
|
|
57
84
|
|
|
58
85
|
1. Generate a random 32-byte seed and select `n` blocks to challenge. Both client
|
|
59
86
|
and server independently apply HMAC-SHA256 to the seed to rank all block IDs and
|
|
60
|
-
select the same `n` blocks in the same order
|
|
87
|
+
select the same `n` blocks in the same order: no extra communication needed.
|
|
61
88
|
Use `buildChallenge(n, total)` for an exact block count, or `audit()` for a
|
|
62
89
|
percentage-based shorthand.
|
|
63
90
|
|
|
@@ -76,7 +103,7 @@ proof-of-storage evidence collected over time.
|
|
|
76
103
|
## Quick Start
|
|
77
104
|
|
|
78
105
|
```typescript
|
|
79
|
-
import { PinionProverClient } from '@
|
|
106
|
+
import { PinionProverClient } from '@pinionengineering/prover-client';
|
|
80
107
|
|
|
81
108
|
const client = new PinionProverClient('https://example.com/prover', {
|
|
82
109
|
getToken: async () => myAuthService.getToken(),
|
|
@@ -84,11 +111,13 @@ const client = new PinionProverClient('https://example.com/prover', {
|
|
|
84
111
|
|
|
85
112
|
// ── Setup phase ────────────────────────────────────────────────────────────
|
|
86
113
|
|
|
87
|
-
// Create a key
|
|
114
|
+
// Create a key: returns the key ID and the public half of the key pair.
|
|
88
115
|
const { keyId, publicKey } = await client.createKey();
|
|
89
116
|
|
|
90
|
-
// Tag a pinned CID
|
|
91
|
-
|
|
117
|
+
// Tag a pinned CID: submits the job, then waits (no default deadline) for
|
|
118
|
+
// the server to walk the DAG and store per-block auth tags.
|
|
119
|
+
const tagJob = await client.tag('bafybeigdyrzt...', keyId);
|
|
120
|
+
await client.waitForTag(tagJob.jobId);
|
|
92
121
|
|
|
93
122
|
// Fetch the setup document: public key + block ID lists for all tagged roots.
|
|
94
123
|
const setup = await client.getSetup(keyId);
|
|
@@ -105,7 +134,7 @@ console.log(result.pass); // true = server cryptographically proved it holds you
|
|
|
105
134
|
## Examples
|
|
106
135
|
|
|
107
136
|
`examples/verify.mjs` is a ready-to-run Node.js script covering the full
|
|
108
|
-
flow
|
|
137
|
+
flow: find-or-create a key, tag new CIDs, run one audit round:
|
|
109
138
|
|
|
110
139
|
```sh
|
|
111
140
|
PROVER_URL=https://hydrogen.pinion.build/prover \
|
|
@@ -159,13 +188,13 @@ The private scalar α never leaves the server. `publicKey` is the WireClientSet
|
|
|
159
188
|
blob decoded from base64; you can store it and pass it directly to `verifyProof()`
|
|
160
189
|
without ever calling `getSetup()` again.
|
|
161
190
|
|
|
162
|
-
A single key covers many CIDs
|
|
191
|
+
A single key covers many CIDs: you do not need a new key per file.
|
|
163
192
|
|
|
164
193
|
#### `client.listKeys()` → `ChallengeKeyInfo[]`
|
|
165
194
|
|
|
166
195
|
Returns all keys for the authenticated user. Each entry includes:
|
|
167
|
-
- `blocks_audited
|
|
168
|
-
- `audit_count
|
|
196
|
+
- `blocks_audited`: cumulative blocks challenged across all audit rounds for this key
|
|
197
|
+
- `audit_count`: number of audit rounds completed
|
|
169
198
|
|
|
170
199
|
These are the long-running metrics that show how much proof-of-storage evidence
|
|
171
200
|
has been accumulated over time.
|
|
@@ -178,47 +207,67 @@ Deletes a key and all associated tags.
|
|
|
178
207
|
|
|
179
208
|
### Setup Phase
|
|
180
209
|
|
|
181
|
-
#### `client.tag(root, keyId)` → `
|
|
210
|
+
#### `client.tag(root, keyId)` → `TagSubmission`
|
|
182
211
|
|
|
183
212
|
```typescript
|
|
184
|
-
const {
|
|
213
|
+
const { jobId } = await client.tag(cid, keyId);
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
Instructs the server to walk the IPFS DAG for `root`, compute per-block
|
|
217
|
+
authentication tags, and store them under `keyId`. The CID must be in the
|
|
218
|
+
`"pinned"` lifecycle state for the authenticated account. Submits the job
|
|
219
|
+
and returns immediately — this does not wait for tagging to finish.
|
|
220
|
+
|
|
221
|
+
#### `client.waitForTag(jobId, options?)` → `TagResponse`
|
|
222
|
+
|
|
223
|
+
```typescript
|
|
224
|
+
const { block_ids, block_count } = await client.waitForTag(jobId, {
|
|
225
|
+
onProgress: (progress, status) => console.log(status, progress),
|
|
226
|
+
});
|
|
185
227
|
// Exactly one is populated, depending on the key's protocol:
|
|
186
|
-
// block_ids: string[]
|
|
228
|
+
// block_ids: string[]: base64(CID.Bytes()) for every block, non-chunked
|
|
187
229
|
// protocols only (Ateniese, Erway, BJO), in TagList order
|
|
188
|
-
// block_count: number
|
|
189
|
-
// (SW-Priv, SW-Pub)
|
|
230
|
+
// block_count: number : super-block count, chunked protocols only
|
|
231
|
+
// (SW-Priv, SW-Pub): no per-block manifest is sent at all
|
|
190
232
|
```
|
|
191
233
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
234
|
+
Polls `tagStatus(jobId)` until the job reaches a terminal state. **No
|
|
235
|
+
default deadline** — waits as long as tagging takes unless `options.signal`
|
|
236
|
+
is given and fires first, in which case it throws `TagTimeoutError`. Throws
|
|
237
|
+
`TagFailedError` if the job reaches `"tag-failed"`.
|
|
195
238
|
|
|
196
239
|
For non-chunked protocols, `block_ids` is the full list of blocks for this
|
|
197
240
|
root in the order both client and server will use when ranking against a
|
|
198
|
-
challenge seed. For chunked protocols, `block_count` is all the client needs
|
|
199
|
-
|
|
241
|
+
challenge seed. For chunked protocols, `block_count` is all the client needs:
|
|
242
|
+
challenge ids are synthesized locally via `superBlockId(rootBytes, i)` for
|
|
200
243
|
`i` in `[0, block_count)`. Call `getSetup()` after tagging to get the
|
|
201
244
|
combined list/count across all roots.
|
|
202
245
|
|
|
246
|
+
| `WaitForTagOptions` field | Type | Default | Description |
|
|
247
|
+
|-------|------|---------|-------------|
|
|
248
|
+
| `pollIntervalMs` | `number` | `1000` | Milliseconds between status polls. |
|
|
249
|
+
| `signal` | `AbortSignal` | none | Optional cancellation — no default deadline. |
|
|
250
|
+
| `onProgress` | `(progress, status) => void` | none | Called after every poll with `{total_blocks, completed_blocks}` and the raw status string. |
|
|
251
|
+
|
|
203
252
|
#### `client.getSetup(keyId)` → `ParsedSetup`
|
|
204
253
|
|
|
205
254
|
Fetches the complete setup document for a key.
|
|
206
255
|
|
|
207
256
|
```typescript
|
|
208
257
|
const setup = await client.getSetup(keyId);
|
|
209
|
-
// setup.clientSetup WireClientSetup
|
|
258
|
+
// setup.clientSetup WireClientSetup: public key material
|
|
210
259
|
// setup.roots [{ root: string, blockIds: Uint8Array[], chunked: boolean }, ...]
|
|
211
260
|
// setup.totalBlocks total block count across all roots
|
|
212
261
|
```
|
|
213
262
|
|
|
214
263
|
`blockIds` is always ready to pass to `buildChallenge()`/`verifyProof()`
|
|
215
264
|
regardless of protocol. `chunked` tells you whether those ids are real
|
|
216
|
-
per-block CIDs (`false`) or synthesized super-block ids (`true`)
|
|
265
|
+
per-block CIDs (`false`) or synthesized super-block ids (`true`): only
|
|
217
266
|
relevant if you need to re-derive or re-export ids yourself; synthesized ids
|
|
218
267
|
are not valid CIDs and must never be round-tripped through `CID.decode()`.
|
|
219
268
|
|
|
220
269
|
Call this once after tagging. Re-call whenever you add or remove roots.
|
|
221
|
-
Pass the returned `ParsedSetup` directly to `audit()
|
|
270
|
+
Pass the returned `ParsedSetup` directly to `audit()`: it does not fetch
|
|
222
271
|
the setup for you.
|
|
223
272
|
|
|
224
273
|
#### `client.deregister(keyId, root)`
|
|
@@ -268,13 +317,17 @@ const result = await client.audit(keyId, setup, {
|
|
|
268
317
|
|-------|------|---------|-------------|
|
|
269
318
|
| `roots` | `string[]` | all roots | Subset of registered CIDs to challenge. |
|
|
270
319
|
| `challengePct` | `number` | `1` | Percentage of blocks to sample (0–100). |
|
|
320
|
+
| `pollIntervalMs` | `number` | `500` | Milliseconds between status polls during the wait phase. |
|
|
321
|
+
| `signal` | `AbortSignal` | none | Optional cancellation — no default deadline, waits as long as the proof takes unless this fires. Pass `AbortSignal.timeout(ms)` for a fixed ceiling. |
|
|
322
|
+
| `onStatus` | `(status: string) => void` | none | Called after every poll with the raw status string — proving has no per-block progress signal, unlike tagging. |
|
|
271
323
|
|
|
272
324
|
Throws `PinNotActiveError` if any root is no longer in the `"pinned"` state.
|
|
273
|
-
Throws `ProverError` on HTTP errors from the server.
|
|
325
|
+
Throws `ProverError` on HTTP errors from the server. Throws
|
|
326
|
+
`ProveTimeoutError` if `options.signal` fires before the proof completes.
|
|
274
327
|
|
|
275
328
|
---
|
|
276
329
|
|
|
277
|
-
#### Low-level: `buildChallenge` + `prove` + `verifyProof`
|
|
330
|
+
#### Low-level: `buildChallenge` + `prove` + `waitForProve` + `verifyProof`
|
|
278
331
|
|
|
279
332
|
Use these when you need an exact block count or want full control over the
|
|
280
333
|
challenge parameters.
|
|
@@ -282,7 +335,7 @@ challenge parameters.
|
|
|
282
335
|
##### `buildChallenge(n, totalBlocks)` → `string`
|
|
283
336
|
|
|
284
337
|
```typescript
|
|
285
|
-
import { buildChallenge } from '@
|
|
338
|
+
import { buildChallenge } from '@pinionengineering/prover-client';
|
|
286
339
|
|
|
287
340
|
const challenge = buildChallenge(10, setup.totalBlocks);
|
|
288
341
|
// Returns base64(JSON({ suite_id: 1, seed: <32 random bytes>, c: 10, n: totalBlocks }))
|
|
@@ -292,18 +345,34 @@ Generates a random 32-byte seed and encodes a WireChallenge requesting `n` block
|
|
|
292
345
|
out of `totalBlocks`. Both client and server independently apply HMAC-SHA256 to
|
|
293
346
|
the seed to rank all block IDs and arrive at the same `n` blocks in the same order.
|
|
294
347
|
|
|
295
|
-
##### `client.prove(keyId, roots, challenge)` → `
|
|
348
|
+
##### `client.prove(keyId, roots, challenge)` → `ProveSubmission`
|
|
349
|
+
|
|
350
|
+
```typescript
|
|
351
|
+
const { jobId } = await client.prove(keyId, roots, challenge);
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
Posts the challenge to `POST /prove` and returns immediately with a job
|
|
355
|
+
handle — this does not wait for the proof to be computed.
|
|
356
|
+
|
|
357
|
+
##### `client.waitForProve(jobId, options?)` → `Uint8Array`
|
|
296
358
|
|
|
297
359
|
```typescript
|
|
298
|
-
const proofBytes = await client.
|
|
360
|
+
const proofBytes = await client.waitForProve(jobId, {
|
|
361
|
+
challenge, roots, // optional, but lets a thrown error carry enough to retry
|
|
362
|
+
signal: AbortSignal.timeout(60_000), // optional — no default deadline otherwise
|
|
363
|
+
});
|
|
299
364
|
```
|
|
300
365
|
|
|
301
|
-
|
|
366
|
+
Polls `proveStatus(jobId)` until the job reaches a terminal state and
|
|
367
|
+
returns the raw proof bytes. **No default deadline** — waits as long as the
|
|
368
|
+
proof takes unless `options.signal` fires first, in which case it throws
|
|
369
|
+
`ProveTimeoutError`. Throws `ProveFailedError` if the job reaches
|
|
370
|
+
`"prove-failed"`.
|
|
302
371
|
|
|
303
372
|
##### `verifyProof(params)` → `boolean`
|
|
304
373
|
|
|
305
374
|
```typescript
|
|
306
|
-
import { verifyProof } from '@
|
|
375
|
+
import { verifyProof } from '@pinionengineering/prover-client';
|
|
307
376
|
|
|
308
377
|
const pass = verifyProof({
|
|
309
378
|
clientSetup: setup.clientSetup, // or publicKey from createKey()
|
|
@@ -314,26 +383,28 @@ const pass = verifyProof({
|
|
|
314
383
|
```
|
|
315
384
|
|
|
316
385
|
Runs the BN254 pairing check locally. Returns `true` only if
|
|
317
|
-
`e(σ,G₂) == e(A,V)`. Safe to call without a client instance
|
|
386
|
+
`e(σ,G₂) == e(A,V)`. Safe to call without a client instance: useful for
|
|
318
387
|
offline verification or integration with existing infrastructure.
|
|
319
388
|
|
|
320
389
|
##### Full low-level example
|
|
321
390
|
|
|
322
391
|
```typescript
|
|
323
|
-
import { PinionProverClient, buildChallenge, verifyProof } from '@
|
|
392
|
+
import { PinionProverClient, buildChallenge, verifyProof } from '@pinionengineering/prover-client';
|
|
324
393
|
|
|
325
394
|
const client = new PinionProverClient(proverUrl, { getToken });
|
|
326
395
|
|
|
327
396
|
// Setup phase
|
|
328
397
|
const { keyId } = await client.createKey();
|
|
329
|
-
await client.tag(cid, keyId);
|
|
398
|
+
const tagJob = await client.tag(cid, keyId);
|
|
399
|
+
await client.waitForTag(tagJob.jobId);
|
|
330
400
|
const setup = await client.getSetup(keyId);
|
|
331
401
|
|
|
332
|
-
// Audit phase
|
|
402
|
+
// Audit phase: exact block count
|
|
333
403
|
const root = setup.roots[0]!;
|
|
334
404
|
const blockIds = root.blockIds;
|
|
335
|
-
const challenge
|
|
336
|
-
const
|
|
405
|
+
const challenge = buildChallenge(10, blockIds.length);
|
|
406
|
+
const submission = await client.prove(keyId, [root.root], challenge);
|
|
407
|
+
const proofBytes = await client.waitForProve(submission.jobId, { challenge, roots: [root.root] });
|
|
337
408
|
|
|
338
409
|
const pass = verifyProof({
|
|
339
410
|
clientSetup: setup.clientSetup,
|
|
@@ -351,7 +422,7 @@ You can verify a proof with no HTTP client at all, as long as you have the
|
|
|
351
422
|
public key and block IDs from a prior setup call:
|
|
352
423
|
|
|
353
424
|
```typescript
|
|
354
|
-
import { buildChallenge, verifyProof, parseClientSetup } from '@
|
|
425
|
+
import { buildChallenge, verifyProof, parseClientSetup } from '@pinionengineering/prover-client';
|
|
355
426
|
|
|
356
427
|
const clientSetup = parseClientSetup(storedClientSetupBase64);
|
|
357
428
|
|
|
@@ -429,7 +500,7 @@ base64( JSON({ protocol, suite_id, s, l, name, v, u }) )
|
|
|
429
500
|
| `suite_id` | `uint8` | `1` |
|
|
430
501
|
| `s` | `int` | Number of sectors per block |
|
|
431
502
|
| `l` | `int` | Challenge size parameter (from the SW-Pub scheme) |
|
|
432
|
-
| `name` | `base64(16 bytes)` | File name
|
|
503
|
+
| `name` | `base64(16 bytes)` | File name λ: random, unique per key, bound into every block tag |
|
|
433
504
|
| `v` | `base64(128 bytes)` | Public key V = α·G₂ (G2 point, EIP-197 format) |
|
|
434
505
|
| `u` | `base64(64 bytes)[]` | Public key U[0..s-1] (G1 points) |
|
|
435
506
|
|
|
@@ -460,13 +531,13 @@ e(σ, G₂) == e(Σₜ νₜ·H(λ‖idᵢₜ) + Σⱼ μⱼ·uⱼ, V)
|
|
|
460
531
|
|
|
461
532
|
where:
|
|
462
533
|
|
|
463
|
-
-
|
|
464
|
-
- `G
|
|
465
|
-
- `νₜ, i
|
|
466
|
-
- `H(λ‖id)
|
|
467
|
-
-
|
|
468
|
-
- `u
|
|
469
|
-
- `V
|
|
534
|
+
- `σ`: proof accumulator G1 point (from server)
|
|
535
|
+
- `G₂`: G2 generator
|
|
536
|
+
- `νₜ, iₜ`: blinding coefficients and block indices (re-derived from seed)
|
|
537
|
+
- `H(λ‖id)`: RFC 9380 SVDW hash-to-G1 with DST `"sw-pub-v1-BN254G1_XMD:SHA-256_SVDW_RO_"`
|
|
538
|
+
- `μⱼ`: per-sector Zr scalars (from server)
|
|
539
|
+
- `uⱼ`: public key G1 elements (from client_setup)
|
|
540
|
+
- `V`: public key `V = α·G₂` (from client_setup)
|
|
470
541
|
|
|
471
542
|
### Hash-to-G1
|
|
472
543
|
|
|
@@ -481,7 +552,7 @@ points that are indistinguishable from uniform random G1 elements.
|
|
|
481
552
|
|
|
482
553
|
The DST `"sw-pub-v1-BN254G1_XMD:SHA-256_SVDW_RO_"` is fixed for all keys and matches
|
|
483
554
|
the value in `storage-proofs/por/sw/pub.go`. **Changing this DST invalidates all
|
|
484
|
-
existing tags
|
|
555
|
+
existing tags**: both sides derive H independently so they must agree.
|
|
485
556
|
|
|
486
557
|
### Challenge Derivation (`DeriveChallenge`)
|
|
487
558
|
|
|
@@ -514,12 +585,18 @@ npm run test:gen
|
|
|
514
585
|
npm test
|
|
515
586
|
```
|
|
516
587
|
|
|
517
|
-
|
|
588
|
+
`test/verify.test.mjs` verifies the cross-language crypto correctness:
|
|
518
589
|
1. Valid proof accepted
|
|
519
590
|
2. Tampered sigma rejected
|
|
520
591
|
3. Wrong block IDs rejected
|
|
521
592
|
4. Wrong public key rejected
|
|
522
593
|
|
|
594
|
+
`test/client.test.mjs` covers `PinionProverClient`'s submit/wait polling
|
|
595
|
+
model against a hand-rolled `fetch` stub: `prove()`/`tag()` submit without
|
|
596
|
+
polling, `waitForProve()`/`waitForTag()` poll to completion with no
|
|
597
|
+
implicit deadline, an `AbortSignal` cancels promptly, and `audit()`'s
|
|
598
|
+
`onStatus`/`pollIntervalMs` options reach the underlying poll.
|
|
599
|
+
|
|
523
600
|
---
|
|
524
601
|
|
|
525
602
|
## Implementation Notes
|
package/dist/challenge.d.ts
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* verification paths that disagree with the server.
|
|
12
12
|
*/
|
|
13
13
|
import { type G1Point } from './bn254.js';
|
|
14
|
+
import type { WireChallenge } from './types.js';
|
|
14
15
|
/**
|
|
15
16
|
* BN254 (alt_bn128 / Ethereum) subgroup order q.
|
|
16
17
|
* = 21888242871839275222246405745257275088548364400416034343698204186575808495617
|
|
@@ -31,6 +32,13 @@ export declare function base64ToBytes(b64: string): Uint8Array;
|
|
|
31
32
|
* @param totalBlocks Total blocks in the challenged store.
|
|
32
33
|
*/
|
|
33
34
|
export declare function buildChallenge(challengeSize: number, totalBlocks: number): string;
|
|
35
|
+
/**
|
|
36
|
+
* Decode a base64(JSON(WireChallenge)) string — the same shape buildChallenge()
|
|
37
|
+
* produces — back into a WireChallenge, for displaying/reporting a challenge
|
|
38
|
+
* already built (AuditResult.challenge, or a caught ProveFailedError/
|
|
39
|
+
* ProveTimeoutError's .challenge), not for building a new one.
|
|
40
|
+
*/
|
|
41
|
+
export declare function decodeChallenge(challengeBase64: string): WireChallenge;
|
|
34
42
|
/**
|
|
35
43
|
* Re-derive the challenge indices and blinding coefficients from a seed.
|
|
36
44
|
*
|
package/dist/challenge.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"challenge.d.ts","sourceRoot":"","sources":["../src/challenge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,EAA2B,KAAK,OAAO,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"challenge.d.ts","sourceRoot":"","sources":["../src/challenge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,EAA2B,KAAK,OAAO,EAAE,MAAM,YAAY,CAAC;AACnE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD;;;GAGG;AACH,eAAO,MAAM,WAAW,iFACwD,CAAC;AAMjF,8BAA8B;AAC9B,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAIvD;AAED,uCAAuC;AACvC,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAKrD;AAMD;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,aAAa,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CASjF;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,eAAe,EAAE,MAAM,GAAG,aAAa,CAGtE;AAMD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,UAAU,EAChB,GAAG,EAAE,UAAU,EAAE,EACjB,CAAC,EAAE,MAAM,GACR;IAAE,OAAO,EAAE,MAAM,EAAE,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,CAoBzC;AAMD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,YAAY,CAAC,SAAS,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,GAAG,UAAU,CAMlF;AAMD;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,UAAU,GAAG,OAAO,CAKrE"}
|
package/dist/challenge.js
CHANGED
|
@@ -59,6 +59,16 @@ export function buildChallenge(challengeSize, totalBlocks) {
|
|
|
59
59
|
};
|
|
60
60
|
return uint8ToBase64(new TextEncoder().encode(JSON.stringify(wireChal)));
|
|
61
61
|
}
|
|
62
|
+
/**
|
|
63
|
+
* Decode a base64(JSON(WireChallenge)) string — the same shape buildChallenge()
|
|
64
|
+
* produces — back into a WireChallenge, for displaying/reporting a challenge
|
|
65
|
+
* already built (AuditResult.challenge, or a caught ProveFailedError/
|
|
66
|
+
* ProveTimeoutError's .challenge), not for building a new one.
|
|
67
|
+
*/
|
|
68
|
+
export function decodeChallenge(challengeBase64) {
|
|
69
|
+
const bytes = base64ToBytes(challengeBase64);
|
|
70
|
+
return JSON.parse(new TextDecoder().decode(bytes));
|
|
71
|
+
}
|
|
62
72
|
// ---------------------------------------------------------------------------
|
|
63
73
|
// Deterministic index + coefficient derivation
|
|
64
74
|
// ---------------------------------------------------------------------------
|
package/dist/challenge.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"challenge.js","sourceRoot":"","sources":["../src/challenge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAC1C,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAgB,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"challenge.js","sourceRoot":"","sources":["../src/challenge.ts"],"names":[],"mappings":"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"}
|