@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/dist/client.d.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* Pass the full base URL including the path prefix, e.g.:
|
|
9
9
|
* new PinionProverClient("https://hydrogen.pinion.build/prover", { getToken })
|
|
10
10
|
*/
|
|
11
|
-
import type { AuditResult, ChallengeKeyInfo, CreateKeyResult, ParsedSetup, ProveJobStatusResponse, RawSetupResponse, TagJobListEntry, TagJobProgress, TagJobStatusResponse, TagResponse } from './types.js';
|
|
11
|
+
import type { AuditResult, ChallengeKeyInfo, CreateKeyResult, ParsedSetup, ProveJobStatusResponse, ProveSubmission, RawSetupResponse, TagJobListEntry, TagJobProgress, TagJobStatusResponse, TagResponse, TagSubmission } from './types.js';
|
|
12
12
|
export interface PinionProverClientOptions {
|
|
13
13
|
/**
|
|
14
14
|
* Returns a JWT Bearer token for authenticated endpoints, or null/undefined
|
|
@@ -20,7 +20,7 @@ export interface PinionProverClientOptions {
|
|
|
20
20
|
* Options for the audit() convenience wrapper.
|
|
21
21
|
*
|
|
22
22
|
* For exact control over block count, use buildChallenge(n, total) +
|
|
23
|
-
* prove() + verifyProof() directly.
|
|
23
|
+
* prove() + waitForProve() + verifyProof() directly.
|
|
24
24
|
*/
|
|
25
25
|
export interface AuditOptions {
|
|
26
26
|
/**
|
|
@@ -36,13 +36,31 @@ export interface AuditOptions {
|
|
|
36
36
|
* For an exact block count use buildChallenge(n, total) + prove() + verifyProof().
|
|
37
37
|
*/
|
|
38
38
|
challengePct?: number;
|
|
39
|
+
/** Milliseconds between GET /prove/:job_id polls during audit()'s wait
|
|
40
|
+
* phase. Default 500. */
|
|
41
|
+
pollIntervalMs?: number;
|
|
42
|
+
/**
|
|
43
|
+
* Optional cancellation of audit()'s wait phase. There is no default
|
|
44
|
+
* deadline — omit to wait as long as the proof takes. Pass
|
|
45
|
+
* `AbortSignal.timeout(ms)` to restore a fixed ceiling.
|
|
46
|
+
*/
|
|
47
|
+
signal?: AbortSignal;
|
|
48
|
+
/** Called after every status poll with the raw status string
|
|
49
|
+
* ("prove-queued" | "prove-running") — for progress UI. There is no
|
|
50
|
+
* per-block progress signal for proving, unlike tag()'s onProgress. */
|
|
51
|
+
onStatus?: (status: string) => void;
|
|
39
52
|
}
|
|
40
|
-
/** Options for
|
|
41
|
-
export interface
|
|
53
|
+
/** Options for waitForTag()'s job-status polling. */
|
|
54
|
+
export interface WaitForTagOptions {
|
|
42
55
|
/** Milliseconds between GET /tag/:job_id polls. Default 1000. */
|
|
43
56
|
pollIntervalMs?: number;
|
|
44
|
-
/**
|
|
45
|
-
|
|
57
|
+
/**
|
|
58
|
+
* Optional cancellation. There is no default deadline — omit to wait
|
|
59
|
+
* indefinitely. Pass `AbortSignal.timeout(ms)` to restore the old fixed
|
|
60
|
+
* ceiling (waitForTag() throws TagTimeoutError when the signal fires
|
|
61
|
+
* before the job reaches a terminal state).
|
|
62
|
+
*/
|
|
63
|
+
signal?: AbortSignal;
|
|
46
64
|
/**
|
|
47
65
|
* Called after every status poll with the latest progress and the raw
|
|
48
66
|
* status string ("tag-queued" | "tag-planning" | "tag-running" |
|
|
@@ -54,17 +72,28 @@ export interface TagOptions {
|
|
|
54
72
|
*/
|
|
55
73
|
onProgress?: (progress: TagJobProgress, status: string) => void;
|
|
56
74
|
}
|
|
57
|
-
/** Options for
|
|
58
|
-
export interface
|
|
75
|
+
/** Options for waitForProve()'s job-status polling. */
|
|
76
|
+
export interface WaitForProveOptions {
|
|
59
77
|
/** Milliseconds between GET /prove/:job_id polls. Default 500. */
|
|
60
78
|
pollIntervalMs?: number;
|
|
61
79
|
/**
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
80
|
+
* Optional cancellation. There is no default deadline — omit to wait
|
|
81
|
+
* indefinitely. Pass `AbortSignal.timeout(ms)` to restore the old fixed
|
|
82
|
+
* ceiling (waitForProve() throws ProveTimeoutError when the signal fires
|
|
83
|
+
* before the job reaches a terminal state).
|
|
66
84
|
*/
|
|
67
|
-
|
|
85
|
+
signal?: AbortSignal;
|
|
86
|
+
/**
|
|
87
|
+
* The challenge/roots this job was submitted with. Optional, but passing
|
|
88
|
+
* them lets a thrown ProveFailedError/ProveTimeoutError carry enough to
|
|
89
|
+
* reconstruct a retry — pass what prove() gave you back.
|
|
90
|
+
*/
|
|
91
|
+
challenge?: string;
|
|
92
|
+
roots?: string[];
|
|
93
|
+
/** Called after every status poll with the raw status string
|
|
94
|
+
* ("prove-queued" | "prove-running"). There is no per-block progress
|
|
95
|
+
* signal for proving, unlike waitForTag()'s onProgress. */
|
|
96
|
+
onStatus?: (status: string) => void;
|
|
68
97
|
}
|
|
69
98
|
export declare class PinionProverClient {
|
|
70
99
|
private readonly baseUrl;
|
|
@@ -98,20 +127,27 @@ export declare class PinionProverClient {
|
|
|
98
127
|
* authentication tags, and store them under keyId.
|
|
99
128
|
*
|
|
100
129
|
* The root must already be in the "pinned" lifecycle state for the
|
|
101
|
-
* authenticated account.
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
* next audit
|
|
107
|
-
*
|
|
108
|
-
* Throws TagFailedError if the job reaches "tag-failed", or
|
|
109
|
-
* TagTimeoutError if it doesn't reach a terminal state within
|
|
110
|
-
* options.timeoutMs.
|
|
130
|
+
* authenticated account. Tagging runs asynchronously on the server — this
|
|
131
|
+
* submits the job and returns immediately with a job handle. Poll
|
|
132
|
+
* tagStatus(jobId) yourself, or await waitForTag(jobId, options) to wait
|
|
133
|
+
* for a terminal state (with no default deadline — pass `signal:
|
|
134
|
+
* AbortSignal.timeout(ms)` if you want one). Call getSetup() after
|
|
135
|
+
* tagging completes to get the updated block ID lists for the next audit
|
|
136
|
+
* cycle.
|
|
111
137
|
*/
|
|
112
|
-
tag(root: string, keyId: string
|
|
113
|
-
/** Poll the status of a tag job started by tag(). Exposed for callers that want progress UI without waiting on
|
|
138
|
+
tag(root: string, keyId: string): Promise<TagSubmission>;
|
|
139
|
+
/** Poll the status of a tag job started by tag(). Exposed for callers that want progress UI without waiting on waitForTag()'s full promise. */
|
|
114
140
|
tagStatus(jobId: string): Promise<TagJobStatusResponse>;
|
|
141
|
+
/**
|
|
142
|
+
* Wait for a tag job started by tag() to reach a terminal state, polling
|
|
143
|
+
* tagStatus(jobId) on an interval.
|
|
144
|
+
*
|
|
145
|
+
* There is no default deadline — this waits as long as the job takes
|
|
146
|
+
* unless options.signal is given and fires first, in which case it
|
|
147
|
+
* throws TagTimeoutError with the last-seen status. Throws TagFailedError
|
|
148
|
+
* if the job reaches "tag-failed".
|
|
149
|
+
*/
|
|
150
|
+
waitForTag(jobId: string, options?: WaitForTagOptions): Promise<TagResponse>;
|
|
115
151
|
/**
|
|
116
152
|
* List the caller's tag jobs, most recently created first.
|
|
117
153
|
*
|
|
@@ -129,34 +165,42 @@ export declare class PinionProverClient {
|
|
|
129
165
|
/**
|
|
130
166
|
* POST /prove — unauthenticated, the server resolves the account from key_id.
|
|
131
167
|
*
|
|
132
|
-
* Proving is asynchronous: this
|
|
133
|
-
*
|
|
134
|
-
*
|
|
135
|
-
*
|
|
136
|
-
* should use audit() instead, which
|
|
137
|
-
* response.
|
|
168
|
+
* Proving is asynchronous: this submits the challenge and returns
|
|
169
|
+
* immediately with a job handle. Poll proveStatus(jobId) yourself, or
|
|
170
|
+
* await waitForProve(jobId, options) to wait for a terminal state (with
|
|
171
|
+
* no default deadline — pass `signal: AbortSignal.timeout(ms)` if you
|
|
172
|
+
* want one). Most callers should use audit() instead, which submits,
|
|
173
|
+
* waits, and cryptographically verifies the response in one call.
|
|
138
174
|
*
|
|
139
175
|
* @param keyId Challenge key ID.
|
|
140
176
|
* @param roots CID strings to prove, in the same order as the challenge.
|
|
141
177
|
* @param challenge base64(JSON(WireChallenge)) from buildChallenge().
|
|
142
178
|
* @param challengeId Optional idempotency key. If a caller's own retry
|
|
143
179
|
* logic re-calls prove() for what is logically the
|
|
144
|
-
* same request (e.g. after
|
|
145
|
-
* outcome), passing the
|
|
146
|
-
*
|
|
147
|
-
*
|
|
148
|
-
*
|
|
149
|
-
*
|
|
150
|
-
* against a previous one.
|
|
180
|
+
* same request (e.g. after giving up waiting on an
|
|
181
|
+
* earlier attempt with an unclear outcome), passing the
|
|
182
|
+
* same challengeId across those attempts makes the
|
|
183
|
+
* server return the original job instead of starting a
|
|
184
|
+
* redundant one. Leave unset for normal audit rounds —
|
|
185
|
+
* each is a fresh, independently random challenge,
|
|
186
|
+
* which should never be deduped against a previous one.
|
|
151
187
|
*
|
|
152
188
|
* Throws PinNotActiveError (409, checked synchronously before any job is
|
|
153
|
-
* created)
|
|
154
|
-
* ProveTimeoutError if it doesn't reach a terminal state within
|
|
155
|
-
* options.timeoutMs.
|
|
189
|
+
* created) or ProverError for any other non-2xx submit response.
|
|
156
190
|
*/
|
|
157
|
-
prove(keyId: string, roots: string[], challenge: string, challengeId?: string
|
|
158
|
-
/** Poll the status of a proof job started by prove(). Exposed for callers that want to observe progress without waiting on
|
|
191
|
+
prove(keyId: string, roots: string[], challenge: string, challengeId?: string): Promise<ProveSubmission>;
|
|
192
|
+
/** Poll the status of a proof job started by prove(). Exposed for callers that want to observe progress without waiting on waitForProve()'s full promise. */
|
|
159
193
|
proveStatus(jobId: string): Promise<ProveJobStatusResponse>;
|
|
194
|
+
/**
|
|
195
|
+
* Wait for a proof job started by prove() to reach a terminal state,
|
|
196
|
+
* polling proveStatus(jobId) on an interval. Returns the raw proof bytes.
|
|
197
|
+
*
|
|
198
|
+
* There is no default deadline — this waits as long as the proof takes
|
|
199
|
+
* unless options.signal is given and fires first, in which case it
|
|
200
|
+
* throws ProveTimeoutError with the last-seen status. Throws
|
|
201
|
+
* ProveFailedError if the job reaches "prove-failed".
|
|
202
|
+
*/
|
|
203
|
+
waitForProve(jobId: string, options?: WaitForProveOptions): Promise<Uint8Array>;
|
|
160
204
|
/**
|
|
161
205
|
* Run one audit round against a pre-fetched setup.
|
|
162
206
|
*
|
|
@@ -176,6 +220,10 @@ export declare class PinionProverClient {
|
|
|
176
220
|
* const result = await client.audit(keyId, setup, { challengePct: 100 });
|
|
177
221
|
* ```
|
|
178
222
|
*
|
|
223
|
+
* Waits for the proof with no default deadline — pass `options.signal` if
|
|
224
|
+
* you want to cancel or bound how long this waits (e.g.
|
|
225
|
+
* `AbortSignal.timeout(60_000)` for the old fixed ceiling).
|
|
226
|
+
*
|
|
179
227
|
* Throws `PinNotActiveError` if any challenged root is no longer pinned.
|
|
180
228
|
*/
|
|
181
229
|
audit(keyId: string, setup: ParsedSetup, options?: AuditOptions): Promise<AuditResult>;
|
|
@@ -214,29 +262,49 @@ export declare class PinNotActiveError extends Error {
|
|
|
214
262
|
readonly cid: string;
|
|
215
263
|
constructor(cid: string);
|
|
216
264
|
}
|
|
217
|
-
/** Thrown by
|
|
265
|
+
/** Thrown by waitForTag() when the async tag job reaches the "tag-failed" state. */
|
|
218
266
|
export declare class TagFailedError extends Error {
|
|
219
267
|
readonly jobId: string;
|
|
220
268
|
readonly reason: string;
|
|
221
269
|
constructor(jobId: string, reason: string);
|
|
222
270
|
}
|
|
223
|
-
/**
|
|
271
|
+
/**
|
|
272
|
+
* Thrown by waitForTag() when its `options.signal` fires before the job
|
|
273
|
+
* reaches a terminal state. There is no default deadline anymore — this
|
|
274
|
+
* only happens if the caller opted into one (e.g. `signal:
|
|
275
|
+
* AbortSignal.timeout(ms)`).
|
|
276
|
+
*/
|
|
224
277
|
export declare class TagTimeoutError extends Error {
|
|
225
278
|
readonly jobId: string;
|
|
226
279
|
readonly lastStatus: string;
|
|
227
280
|
constructor(jobId: string, lastStatus: string);
|
|
228
281
|
}
|
|
229
|
-
/** Thrown by
|
|
282
|
+
/** Thrown by waitForProve() when the async proof job reaches the "prove-failed" state. */
|
|
230
283
|
export declare class ProveFailedError extends Error {
|
|
231
284
|
readonly jobId: string;
|
|
232
285
|
readonly reason: string;
|
|
233
|
-
|
|
286
|
+
/** The challenge this job was for — base64(JSON(WireChallenge)), decode with decodeChallenge(). Undefined if the caller didn't pass one to waitForProve(). */
|
|
287
|
+
readonly challenge: string | undefined;
|
|
288
|
+
readonly roots: string[] | undefined;
|
|
289
|
+
constructor(jobId: string, reason: string,
|
|
290
|
+
/** The challenge this job was for — base64(JSON(WireChallenge)), decode with decodeChallenge(). Undefined if the caller didn't pass one to waitForProve(). */
|
|
291
|
+
challenge: string | undefined, roots: string[] | undefined);
|
|
234
292
|
}
|
|
235
|
-
/**
|
|
293
|
+
/**
|
|
294
|
+
* Thrown by waitForProve() when its `options.signal` fires before the job
|
|
295
|
+
* reaches a terminal state. There is no default deadline anymore — this
|
|
296
|
+
* only happens if the caller opted into one (e.g. `signal:
|
|
297
|
+
* AbortSignal.timeout(ms)`).
|
|
298
|
+
*/
|
|
236
299
|
export declare class ProveTimeoutError extends Error {
|
|
237
300
|
readonly jobId: string;
|
|
238
301
|
readonly lastStatus: string;
|
|
239
|
-
|
|
302
|
+
/** The challenge this job was for — base64(JSON(WireChallenge)), decode with decodeChallenge(). Undefined if the caller didn't pass one to waitForProve(). */
|
|
303
|
+
readonly challenge: string | undefined;
|
|
304
|
+
readonly roots: string[] | undefined;
|
|
305
|
+
constructor(jobId: string, lastStatus: string,
|
|
306
|
+
/** The challenge this job was for — base64(JSON(WireChallenge)), decode with decodeChallenge(). Undefined if the caller didn't pass one to waitForProve(). */
|
|
307
|
+
challenge: string | undefined, roots: string[] | undefined);
|
|
240
308
|
}
|
|
241
309
|
/**
|
|
242
310
|
* Decode a raw /setup response into a ParsedSetup.
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EACV,WAAW,EACX,gBAAgB,EAEhB,eAAe,EACf,WAAW,EAGX,sBAAsB,EACtB,gBAAgB,EAChB,eAAe,EAEf,cAAc,EAEd,oBAAoB,EACpB,WAAW,
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/dist/client.js
CHANGED
|
@@ -71,45 +71,57 @@ export class PinionProverClient {
|
|
|
71
71
|
* authentication tags, and store them under keyId.
|
|
72
72
|
*
|
|
73
73
|
* The root must already be in the "pinned" lifecycle state for the
|
|
74
|
-
* authenticated account.
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
* next audit
|
|
80
|
-
*
|
|
81
|
-
* Throws TagFailedError if the job reaches "tag-failed", or
|
|
82
|
-
* TagTimeoutError if it doesn't reach a terminal state within
|
|
83
|
-
* options.timeoutMs.
|
|
74
|
+
* authenticated account. Tagging runs asynchronously on the server — this
|
|
75
|
+
* submits the job and returns immediately with a job handle. Poll
|
|
76
|
+
* tagStatus(jobId) yourself, or await waitForTag(jobId, options) to wait
|
|
77
|
+
* for a terminal state (with no default deadline — pass `signal:
|
|
78
|
+
* AbortSignal.timeout(ms)` if you want one). Call getSetup() after
|
|
79
|
+
* tagging completes to get the updated block ID lists for the next audit
|
|
80
|
+
* cycle.
|
|
84
81
|
*/
|
|
85
|
-
async tag(root, keyId
|
|
86
|
-
const pollIntervalMs = options.pollIntervalMs ?? 1000;
|
|
87
|
-
const timeoutMs = options.timeoutMs ?? 10 * 60 * 1000;
|
|
82
|
+
async tag(root, keyId) {
|
|
88
83
|
const { job_id: jobId } = await this.post('/api/v1/tag', {
|
|
89
84
|
root,
|
|
90
85
|
key_id: keyId,
|
|
91
86
|
});
|
|
92
|
-
|
|
93
|
-
for (;;) {
|
|
94
|
-
const status = await this.tagStatus(jobId);
|
|
95
|
-
if (status.progress)
|
|
96
|
-
options.onProgress?.(status.progress, status.status);
|
|
97
|
-
if (status.status === 'tag-done') {
|
|
98
|
-
return { block_ids: status.block_ids, block_count: status.block_count };
|
|
99
|
-
}
|
|
100
|
-
if (status.status === 'tag-failed') {
|
|
101
|
-
throw new TagFailedError(jobId, status.error ?? 'unknown error');
|
|
102
|
-
}
|
|
103
|
-
if (Date.now() >= deadline) {
|
|
104
|
-
throw new TagTimeoutError(jobId, status.status);
|
|
105
|
-
}
|
|
106
|
-
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
|
|
107
|
-
}
|
|
87
|
+
return { jobId, root, keyId };
|
|
108
88
|
}
|
|
109
|
-
/** Poll the status of a tag job started by tag(). Exposed for callers that want progress UI without waiting on
|
|
89
|
+
/** Poll the status of a tag job started by tag(). Exposed for callers that want progress UI without waiting on waitForTag()'s full promise. */
|
|
110
90
|
async tagStatus(jobId) {
|
|
111
91
|
return this.get(`/api/v1/tag/${encodeURIComponent(jobId)}`);
|
|
112
92
|
}
|
|
93
|
+
/**
|
|
94
|
+
* Wait for a tag job started by tag() to reach a terminal state, polling
|
|
95
|
+
* tagStatus(jobId) on an interval.
|
|
96
|
+
*
|
|
97
|
+
* There is no default deadline — this waits as long as the job takes
|
|
98
|
+
* unless options.signal is given and fires first, in which case it
|
|
99
|
+
* throws TagTimeoutError with the last-seen status. Throws TagFailedError
|
|
100
|
+
* if the job reaches "tag-failed".
|
|
101
|
+
*/
|
|
102
|
+
async waitForTag(jobId, options = {}) {
|
|
103
|
+
const pollIntervalMs = options.pollIntervalMs ?? 1000;
|
|
104
|
+
const status = await pollUntilTerminal({
|
|
105
|
+
fetchStatus: () => this.tagStatus(jobId),
|
|
106
|
+
isTerminal: (s) => s.status === 'tag-done' || s.status === 'tag-failed',
|
|
107
|
+
pollIntervalMs,
|
|
108
|
+
signal: options.signal,
|
|
109
|
+
onTick: (s) => {
|
|
110
|
+
if (s.progress)
|
|
111
|
+
options.onProgress?.(s.progress, s.status);
|
|
112
|
+
},
|
|
113
|
+
}).catch((e) => {
|
|
114
|
+
if (e instanceof PollAbortedError) {
|
|
115
|
+
const last = e.lastStatus;
|
|
116
|
+
throw new TagTimeoutError(jobId, last?.status ?? 'unknown');
|
|
117
|
+
}
|
|
118
|
+
throw e;
|
|
119
|
+
});
|
|
120
|
+
if (status.status === 'tag-failed') {
|
|
121
|
+
throw new TagFailedError(jobId, status.error ?? 'unknown error');
|
|
122
|
+
}
|
|
123
|
+
return { block_ids: status.block_ids, block_count: status.block_count };
|
|
124
|
+
}
|
|
113
125
|
/**
|
|
114
126
|
* List the caller's tag jobs, most recently created first.
|
|
115
127
|
*
|
|
@@ -134,34 +146,30 @@ export class PinionProverClient {
|
|
|
134
146
|
/**
|
|
135
147
|
* POST /prove — unauthenticated, the server resolves the account from key_id.
|
|
136
148
|
*
|
|
137
|
-
* Proving is asynchronous: this
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
* should use audit() instead, which
|
|
142
|
-
* response.
|
|
149
|
+
* Proving is asynchronous: this submits the challenge and returns
|
|
150
|
+
* immediately with a job handle. Poll proveStatus(jobId) yourself, or
|
|
151
|
+
* await waitForProve(jobId, options) to wait for a terminal state (with
|
|
152
|
+
* no default deadline — pass `signal: AbortSignal.timeout(ms)` if you
|
|
153
|
+
* want one). Most callers should use audit() instead, which submits,
|
|
154
|
+
* waits, and cryptographically verifies the response in one call.
|
|
143
155
|
*
|
|
144
156
|
* @param keyId Challenge key ID.
|
|
145
157
|
* @param roots CID strings to prove, in the same order as the challenge.
|
|
146
158
|
* @param challenge base64(JSON(WireChallenge)) from buildChallenge().
|
|
147
159
|
* @param challengeId Optional idempotency key. If a caller's own retry
|
|
148
160
|
* logic re-calls prove() for what is logically the
|
|
149
|
-
* same request (e.g. after
|
|
150
|
-
* outcome), passing the
|
|
151
|
-
*
|
|
152
|
-
*
|
|
153
|
-
*
|
|
154
|
-
*
|
|
155
|
-
* against a previous one.
|
|
161
|
+
* same request (e.g. after giving up waiting on an
|
|
162
|
+
* earlier attempt with an unclear outcome), passing the
|
|
163
|
+
* same challengeId across those attempts makes the
|
|
164
|
+
* server return the original job instead of starting a
|
|
165
|
+
* redundant one. Leave unset for normal audit rounds —
|
|
166
|
+
* each is a fresh, independently random challenge,
|
|
167
|
+
* which should never be deduped against a previous one.
|
|
156
168
|
*
|
|
157
169
|
* Throws PinNotActiveError (409, checked synchronously before any job is
|
|
158
|
-
* created)
|
|
159
|
-
* ProveTimeoutError if it doesn't reach a terminal state within
|
|
160
|
-
* options.timeoutMs.
|
|
170
|
+
* created) or ProverError for any other non-2xx submit response.
|
|
161
171
|
*/
|
|
162
|
-
async prove(keyId, roots, challenge, challengeId
|
|
163
|
-
const pollIntervalMs = options.pollIntervalMs ?? 500;
|
|
164
|
-
const timeoutMs = options.timeoutMs ?? 60 * 1000;
|
|
172
|
+
async prove(keyId, roots, challenge, challengeId) {
|
|
165
173
|
const resp = await fetch(`${this.baseUrl}/prove`, {
|
|
166
174
|
method: 'POST',
|
|
167
175
|
headers: { 'Content-Type': 'application/json' },
|
|
@@ -175,25 +183,41 @@ export class PinionProverClient {
|
|
|
175
183
|
throw new ProverError(resp.status, await resp.text().catch(() => ''));
|
|
176
184
|
}
|
|
177
185
|
const { job_id: jobId } = await parseJsonBody(resp);
|
|
178
|
-
|
|
179
|
-
for (;;) {
|
|
180
|
-
const status = await this.proveStatus(jobId);
|
|
181
|
-
if (status.status === 'prove-done') {
|
|
182
|
-
return base64ToBytes(status.proof ?? '');
|
|
183
|
-
}
|
|
184
|
-
if (status.status === 'prove-failed') {
|
|
185
|
-
throw new ProveFailedError(jobId, status.error ?? 'unknown error');
|
|
186
|
-
}
|
|
187
|
-
if (Date.now() >= deadline) {
|
|
188
|
-
throw new ProveTimeoutError(jobId, status.status);
|
|
189
|
-
}
|
|
190
|
-
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
|
|
191
|
-
}
|
|
186
|
+
return { jobId, challenge, roots };
|
|
192
187
|
}
|
|
193
|
-
/** Poll the status of a proof job started by prove(). Exposed for callers that want to observe progress without waiting on
|
|
188
|
+
/** Poll the status of a proof job started by prove(). Exposed for callers that want to observe progress without waiting on waitForProve()'s full promise. */
|
|
194
189
|
async proveStatus(jobId) {
|
|
195
190
|
return this.get(`/prove/${encodeURIComponent(jobId)}`);
|
|
196
191
|
}
|
|
192
|
+
/**
|
|
193
|
+
* Wait for a proof job started by prove() to reach a terminal state,
|
|
194
|
+
* polling proveStatus(jobId) on an interval. Returns the raw proof bytes.
|
|
195
|
+
*
|
|
196
|
+
* There is no default deadline — this waits as long as the proof takes
|
|
197
|
+
* unless options.signal is given and fires first, in which case it
|
|
198
|
+
* throws ProveTimeoutError with the last-seen status. Throws
|
|
199
|
+
* ProveFailedError if the job reaches "prove-failed".
|
|
200
|
+
*/
|
|
201
|
+
async waitForProve(jobId, options = {}) {
|
|
202
|
+
const pollIntervalMs = options.pollIntervalMs ?? 500;
|
|
203
|
+
const status = await pollUntilTerminal({
|
|
204
|
+
fetchStatus: () => this.proveStatus(jobId),
|
|
205
|
+
isTerminal: (s) => s.status === 'prove-done' || s.status === 'prove-failed',
|
|
206
|
+
pollIntervalMs,
|
|
207
|
+
signal: options.signal,
|
|
208
|
+
onTick: (s) => options.onStatus?.(s.status),
|
|
209
|
+
}).catch((e) => {
|
|
210
|
+
if (e instanceof PollAbortedError) {
|
|
211
|
+
const last = e.lastStatus;
|
|
212
|
+
throw new ProveTimeoutError(jobId, last?.status ?? 'unknown', options.challenge, options.roots);
|
|
213
|
+
}
|
|
214
|
+
throw e;
|
|
215
|
+
});
|
|
216
|
+
if (status.status === 'prove-failed') {
|
|
217
|
+
throw new ProveFailedError(jobId, status.error ?? 'unknown error', options.challenge, options.roots);
|
|
218
|
+
}
|
|
219
|
+
return base64ToBytes(status.proof ?? '');
|
|
220
|
+
}
|
|
197
221
|
/**
|
|
198
222
|
* Run one audit round against a pre-fetched setup.
|
|
199
223
|
*
|
|
@@ -213,6 +237,10 @@ export class PinionProverClient {
|
|
|
213
237
|
* const result = await client.audit(keyId, setup, { challengePct: 100 });
|
|
214
238
|
* ```
|
|
215
239
|
*
|
|
240
|
+
* Waits for the proof with no default deadline — pass `options.signal` if
|
|
241
|
+
* you want to cancel or bound how long this waits (e.g.
|
|
242
|
+
* `AbortSignal.timeout(60_000)` for the old fixed ceiling).
|
|
243
|
+
*
|
|
216
244
|
* Throws `PinNotActiveError` if any challenged root is no longer pinned.
|
|
217
245
|
*/
|
|
218
246
|
async audit(keyId, setup, options = {}) {
|
|
@@ -231,14 +259,28 @@ export class PinionProverClient {
|
|
|
231
259
|
throw new Error('no blocks to audit');
|
|
232
260
|
const challengeSize = Math.max(1, Math.round((challengePct / 100) * allBlockIds.length));
|
|
233
261
|
const challenge = buildChallenge(challengeSize, allBlockIds.length);
|
|
234
|
-
const
|
|
262
|
+
const submission = await this.prove(keyId, targetRoots, challenge);
|
|
263
|
+
const proofBytes = await this.waitForProve(submission.jobId, {
|
|
264
|
+
pollIntervalMs: options.pollIntervalMs,
|
|
265
|
+
signal: options.signal,
|
|
266
|
+
challenge,
|
|
267
|
+
roots: targetRoots,
|
|
268
|
+
onStatus: options.onStatus,
|
|
269
|
+
});
|
|
235
270
|
const verification = verifyProofResult({
|
|
236
271
|
clientSetup: setup.clientSetup,
|
|
237
272
|
blockIds: allBlockIds,
|
|
238
273
|
challenge,
|
|
239
274
|
proofBytes,
|
|
240
275
|
});
|
|
241
|
-
return {
|
|
276
|
+
return {
|
|
277
|
+
pass: verification.verified,
|
|
278
|
+
verification,
|
|
279
|
+
blocksChecked: challengeSize,
|
|
280
|
+
keyId,
|
|
281
|
+
roots: targetRoots,
|
|
282
|
+
challenge,
|
|
283
|
+
};
|
|
242
284
|
}
|
|
243
285
|
// ---------------------------------------------------------------------------
|
|
244
286
|
// HTTP helpers
|
|
@@ -310,6 +352,57 @@ async function parseJsonBody(resp) {
|
|
|
310
352
|
}
|
|
311
353
|
}
|
|
312
354
|
// ---------------------------------------------------------------------------
|
|
355
|
+
// Shared polling loop for waitForProve()/waitForTag()
|
|
356
|
+
// ---------------------------------------------------------------------------
|
|
357
|
+
/**
|
|
358
|
+
* Internal signal that a poll loop was cancelled via its caller-supplied
|
|
359
|
+
* AbortSignal before the job reached a terminal state. waitForProve()/
|
|
360
|
+
* waitForTag() catch this and rethrow as ProveTimeoutError/TagTimeoutError
|
|
361
|
+
* carrying lastStatus — never thrown to the outside on its own.
|
|
362
|
+
*/
|
|
363
|
+
class PollAbortedError extends Error {
|
|
364
|
+
lastStatus;
|
|
365
|
+
constructor(lastStatus) {
|
|
366
|
+
super('poll aborted');
|
|
367
|
+
this.lastStatus = lastStatus;
|
|
368
|
+
this.name = 'PollAbortedError';
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* Polls fetchStatus() every pollIntervalMs until isTerminal(status) is
|
|
373
|
+
* true, calling onTick(status) after every poll (terminal or not) so
|
|
374
|
+
* callers can drive progress callbacks. Has NO built-in deadline — the
|
|
375
|
+
* caller's polling budget is unlimited unless `signal` is given, in which
|
|
376
|
+
* case an abort rejects with PollAbortedError(lastStatus) instead of
|
|
377
|
+
* resolving.
|
|
378
|
+
*/
|
|
379
|
+
async function pollUntilTerminal(opts) {
|
|
380
|
+
let last;
|
|
381
|
+
for (;;) {
|
|
382
|
+
if (opts.signal?.aborted)
|
|
383
|
+
throw new PollAbortedError(last);
|
|
384
|
+
const status = await opts.fetchStatus();
|
|
385
|
+
last = status;
|
|
386
|
+
opts.onTick?.(status);
|
|
387
|
+
if (opts.isTerminal(status))
|
|
388
|
+
return status;
|
|
389
|
+
await sleepOrAbort(opts.pollIntervalMs, opts.signal, last);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
function sleepOrAbort(ms, signal, lastStatus) {
|
|
393
|
+
return new Promise((resolve, reject) => {
|
|
394
|
+
if (signal?.aborted) {
|
|
395
|
+
reject(new PollAbortedError(lastStatus));
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
const timer = setTimeout(resolve, ms);
|
|
399
|
+
signal?.addEventListener('abort', () => {
|
|
400
|
+
clearTimeout(timer);
|
|
401
|
+
reject(new PollAbortedError(lastStatus));
|
|
402
|
+
}, { once: true });
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
// ---------------------------------------------------------------------------
|
|
313
406
|
// Errors
|
|
314
407
|
// ---------------------------------------------------------------------------
|
|
315
408
|
export class ProverError extends Error {
|
|
@@ -355,7 +448,7 @@ export class PinNotActiveError extends Error {
|
|
|
355
448
|
this.name = 'PinNotActiveError';
|
|
356
449
|
}
|
|
357
450
|
}
|
|
358
|
-
/** Thrown by
|
|
451
|
+
/** Thrown by waitForTag() when the async tag job reaches the "tag-failed" state. */
|
|
359
452
|
export class TagFailedError extends Error {
|
|
360
453
|
jobId;
|
|
361
454
|
reason;
|
|
@@ -366,7 +459,12 @@ export class TagFailedError extends Error {
|
|
|
366
459
|
this.name = 'TagFailedError';
|
|
367
460
|
}
|
|
368
461
|
}
|
|
369
|
-
/**
|
|
462
|
+
/**
|
|
463
|
+
* Thrown by waitForTag() when its `options.signal` fires before the job
|
|
464
|
+
* reaches a terminal state. There is no default deadline anymore — this
|
|
465
|
+
* only happens if the caller opted into one (e.g. `signal:
|
|
466
|
+
* AbortSignal.timeout(ms)`).
|
|
467
|
+
*/
|
|
370
468
|
export class TagTimeoutError extends Error {
|
|
371
469
|
jobId;
|
|
372
470
|
lastStatus;
|
|
@@ -377,25 +475,42 @@ export class TagTimeoutError extends Error {
|
|
|
377
475
|
this.name = 'TagTimeoutError';
|
|
378
476
|
}
|
|
379
477
|
}
|
|
380
|
-
/** Thrown by
|
|
478
|
+
/** Thrown by waitForProve() when the async proof job reaches the "prove-failed" state. */
|
|
381
479
|
export class ProveFailedError extends Error {
|
|
382
480
|
jobId;
|
|
383
481
|
reason;
|
|
384
|
-
|
|
482
|
+
challenge;
|
|
483
|
+
roots;
|
|
484
|
+
constructor(jobId, reason,
|
|
485
|
+
/** The challenge this job was for — base64(JSON(WireChallenge)), decode with decodeChallenge(). Undefined if the caller didn't pass one to waitForProve(). */
|
|
486
|
+
challenge, roots) {
|
|
385
487
|
super(`prove job ${jobId} failed: ${reason}`);
|
|
386
488
|
this.jobId = jobId;
|
|
387
489
|
this.reason = reason;
|
|
490
|
+
this.challenge = challenge;
|
|
491
|
+
this.roots = roots;
|
|
388
492
|
this.name = 'ProveFailedError';
|
|
389
493
|
}
|
|
390
494
|
}
|
|
391
|
-
/**
|
|
495
|
+
/**
|
|
496
|
+
* Thrown by waitForProve() when its `options.signal` fires before the job
|
|
497
|
+
* reaches a terminal state. There is no default deadline anymore — this
|
|
498
|
+
* only happens if the caller opted into one (e.g. `signal:
|
|
499
|
+
* AbortSignal.timeout(ms)`).
|
|
500
|
+
*/
|
|
392
501
|
export class ProveTimeoutError extends Error {
|
|
393
502
|
jobId;
|
|
394
503
|
lastStatus;
|
|
395
|
-
|
|
504
|
+
challenge;
|
|
505
|
+
roots;
|
|
506
|
+
constructor(jobId, lastStatus,
|
|
507
|
+
/** The challenge this job was for — base64(JSON(WireChallenge)), decode with decodeChallenge(). Undefined if the caller didn't pass one to waitForProve(). */
|
|
508
|
+
challenge, roots) {
|
|
396
509
|
super(`prove job ${jobId} timed out (last status: ${lastStatus})`);
|
|
397
510
|
this.jobId = jobId;
|
|
398
511
|
this.lastStatus = lastStatus;
|
|
512
|
+
this.challenge = challenge;
|
|
513
|
+
this.roots = roots;
|
|
399
514
|
this.name = 'ProveTimeoutError';
|
|
400
515
|
}
|
|
401
516
|
}
|