@aztec/prover-client 0.0.1-commit.b9865e97 → 0.0.1-commit.be03c316
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/dest/light/lightweight_checkpoint_builder.d.ts +1 -1
- package/dest/light/lightweight_checkpoint_builder.d.ts.map +1 -1
- package/dest/light/lightweight_checkpoint_builder.js +3 -1
- package/dest/mocks/test_context.d.ts +9 -11
- package/dest/mocks/test_context.d.ts.map +1 -1
- package/dest/mocks/test_context.js +13 -22
- package/dest/orchestrator/block-building-helpers.d.ts +1 -1
- package/dest/orchestrator/checkpoint-proving-state.d.ts +13 -36
- package/dest/orchestrator/checkpoint-proving-state.d.ts.map +1 -1
- package/dest/orchestrator/checkpoint-proving-state.js +18 -125
- package/dest/orchestrator/checkpoint-sub-tree-orchestrator.d.ts +116 -62
- package/dest/orchestrator/checkpoint-sub-tree-orchestrator.d.ts.map +1 -1
- package/dest/orchestrator/checkpoint-sub-tree-orchestrator.js +1005 -90
- package/dest/orchestrator/chonk-cache.d.ts +39 -0
- package/dest/orchestrator/chonk-cache.d.ts.map +1 -0
- package/dest/orchestrator/chonk-cache.js +80 -0
- package/dest/orchestrator/index.d.ts +2 -3
- package/dest/orchestrator/index.d.ts.map +1 -1
- package/dest/orchestrator/index.js +1 -2
- package/dest/orchestrator/proving-scheduler.d.ts +26 -17
- package/dest/orchestrator/proving-scheduler.d.ts.map +1 -1
- package/dest/orchestrator/proving-scheduler.js +28 -25
- package/dest/orchestrator/top-tree-orchestrator.d.ts +11 -6
- package/dest/orchestrator/top-tree-orchestrator.d.ts.map +1 -1
- package/dest/orchestrator/top-tree-orchestrator.js +71 -21
- package/dest/orchestrator/top-tree-proving-state.d.ts +1 -1
- package/dest/orchestrator/top-tree-proving-state.d.ts.map +1 -1
- package/dest/orchestrator/tx-proving-state.d.ts +3 -3
- package/dest/orchestrator/tx-proving-state.d.ts.map +1 -1
- package/dest/prover-client/prover-client.d.ts +17 -19
- package/dest/prover-client/prover-client.d.ts.map +1 -1
- package/dest/prover-client/prover-client.js +24 -25
- package/dest/proving_broker/broker_prover_facade.d.ts +3 -3
- package/dest/proving_broker/broker_prover_facade.d.ts.map +1 -1
- package/dest/test/epoch_settlement.d.ts +36 -0
- package/dest/test/epoch_settlement.d.ts.map +1 -0
- package/dest/test/epoch_settlement.js +52 -0
- package/dest/test/index.d.ts +2 -0
- package/dest/test/index.d.ts.map +1 -0
- package/dest/test/index.js +1 -0
- package/dest/test/mock_prover.d.ts +1 -1
- package/dest/test/mock_prover.js +2 -2
- package/package.json +17 -16
- package/src/light/lightweight_checkpoint_builder.ts +2 -0
- package/src/mocks/test_context.ts +14 -34
- package/src/orchestrator/checkpoint-proving-state.ts +14 -168
- package/src/orchestrator/checkpoint-sub-tree-orchestrator.ts +902 -129
- package/src/orchestrator/chonk-cache.ts +99 -0
- package/src/orchestrator/index.ts +1 -2
- package/src/orchestrator/proving-scheduler.ts +29 -25
- package/src/orchestrator/top-tree-orchestrator.ts +114 -44
- package/src/orchestrator/top-tree-proving-state.ts +0 -1
- package/src/orchestrator/tx-proving-state.ts +3 -3
- package/src/prover-client/prover-client.ts +35 -49
- package/src/proving_broker/broker_prover_facade.ts +2 -2
- package/src/test/epoch_settlement.ts +82 -0
- package/src/test/index.ts +1 -0
- package/src/test/mock_prover.ts +2 -2
- package/dest/orchestrator/epoch-proving-context.d.ts +0 -51
- package/dest/orchestrator/epoch-proving-context.d.ts.map +0 -1
- package/dest/orchestrator/epoch-proving-context.js +0 -81
- package/dest/orchestrator/epoch-proving-state.d.ts +0 -75
- package/dest/orchestrator/epoch-proving-state.d.ts.map +0 -1
- package/dest/orchestrator/epoch-proving-state.js +0 -269
- package/dest/orchestrator/orchestrator.d.ts +0 -118
- package/dest/orchestrator/orchestrator.d.ts.map +0 -1
- package/dest/orchestrator/orchestrator.js +0 -1113
- package/dest/orchestrator/top-tree-proving-scheduler.d.ts +0 -62
- package/dest/orchestrator/top-tree-proving-scheduler.d.ts.map +0 -1
- package/dest/orchestrator/top-tree-proving-scheduler.js +0 -73
- package/dest/prover-client/server-epoch-prover.d.ts +0 -32
- package/dest/prover-client/server-epoch-prover.d.ts.map +0 -1
- package/dest/prover-client/server-epoch-prover.js +0 -40
- package/src/orchestrator/epoch-proving-context.ts +0 -101
- package/src/orchestrator/epoch-proving-state.ts +0 -380
- package/src/orchestrator/orchestrator.ts +0 -1111
- package/src/orchestrator/top-tree-proving-scheduler.ts +0 -154
- package/src/prover-client/server-epoch-prover.ts +0 -69
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import type { NESTED_RECURSIVE_ROLLUP_HONK_PROOF_LENGTH } from '@aztec/constants';
|
|
2
|
+
import { type Logger, type LoggerBindings, createLogger } from '@aztec/foundation/log';
|
|
3
|
+
import type { L2Block } from '@aztec/stdlib/block';
|
|
4
|
+
import type { PublicInputsAndRecursiveProof } from '@aztec/stdlib/interfaces/server';
|
|
5
|
+
import type { PublicChonkVerifierPublicInputs } from '@aztec/stdlib/rollup';
|
|
6
|
+
|
|
7
|
+
/** Result of a chonk-verifier proof, cached by tx hash. */
|
|
8
|
+
export type ChonkVerifierProofResult = PublicInputsAndRecursiveProof<
|
|
9
|
+
PublicChonkVerifierPublicInputs,
|
|
10
|
+
typeof NESTED_RECURSIVE_ROLLUP_HONK_PROOF_LENGTH
|
|
11
|
+
>;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Shared cache of `getPublicChonkVerifierProof` results, keyed by tx hash.
|
|
15
|
+
*
|
|
16
|
+
* Owned by the prover-node and shared across every epoch / session: a tx remined into
|
|
17
|
+
* a different block (e.g. after a brief L1 reorg) reuses the already-computed chonk
|
|
18
|
+
* proof rather than redoing it. Entries are released via `releaseForBlocks` once the
|
|
19
|
+
* containing block is no longer interesting to the caller (e.g. its epoch's proof
|
|
20
|
+
* submission window has expired).
|
|
21
|
+
*/
|
|
22
|
+
export class ChonkCache {
|
|
23
|
+
private readonly cache = new Map<string, Promise<ChonkVerifierProofResult>>();
|
|
24
|
+
private readonly pending = new Map<string, AbortController>();
|
|
25
|
+
private readonly log: Logger;
|
|
26
|
+
private stopped = false;
|
|
27
|
+
|
|
28
|
+
constructor(bindings?: LoggerBindings) {
|
|
29
|
+
this.log = createLogger('prover-client:chonk-cache', bindings);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Returns the cached promise for `txHash`, or `undefined` if none is registered. */
|
|
33
|
+
public get(txHash: string): Promise<ChonkVerifierProofResult> | undefined {
|
|
34
|
+
return this.cache.get(txHash);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Atomic get-or-compute: returns the cached promise for `txHash`, or runs `factory`
|
|
39
|
+
* (passing an AbortSignal the cache controls) and caches its result. Concurrent
|
|
40
|
+
* callers for the same `txHash` share the same promise.
|
|
41
|
+
*
|
|
42
|
+
* Rejected promises are evicted so a future caller can retry. The factory's
|
|
43
|
+
* AbortSignal fires when the cache is stopped.
|
|
44
|
+
*/
|
|
45
|
+
public getOrCompute(
|
|
46
|
+
txHash: string,
|
|
47
|
+
factory: (signal: AbortSignal) => Promise<ChonkVerifierProofResult>,
|
|
48
|
+
): Promise<ChonkVerifierProofResult> {
|
|
49
|
+
if (this.stopped) {
|
|
50
|
+
return Promise.reject(new Error('ChonkCache is stopped'));
|
|
51
|
+
}
|
|
52
|
+
const existing = this.cache.get(txHash);
|
|
53
|
+
if (existing) {
|
|
54
|
+
return existing;
|
|
55
|
+
}
|
|
56
|
+
const controller = new AbortController();
|
|
57
|
+
this.pending.set(txHash, controller);
|
|
58
|
+
this.log.debug(`Enqueueing chonk-verifier circuit`, { txHash });
|
|
59
|
+
const promise = factory(controller.signal).finally(() => this.pending.delete(txHash));
|
|
60
|
+
// Silently observe the rejection branch and evict so a retry is possible.
|
|
61
|
+
promise.catch(err => {
|
|
62
|
+
this.cache.delete(txHash);
|
|
63
|
+
this.log.debug(`Chonk-verifier proof failed; evicted from cache`, { txHash, error: `${err}` });
|
|
64
|
+
});
|
|
65
|
+
this.cache.set(txHash, promise);
|
|
66
|
+
return promise;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Drops cache entries for every tx in the supplied blocks. */
|
|
70
|
+
public releaseForBlocks(blocks: L2Block[]): void {
|
|
71
|
+
let released = 0;
|
|
72
|
+
for (const block of blocks) {
|
|
73
|
+
for (const txEffect of block.body.txEffects) {
|
|
74
|
+
if (this.cache.delete(txEffect.txHash.toString())) {
|
|
75
|
+
released++;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (released > 0) {
|
|
80
|
+
this.log.debug(`Released ${released} chonk-verifier cache entries`, {
|
|
81
|
+
blockCount: blocks.length,
|
|
82
|
+
releasedCount: released,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** Aborts every in-flight chonk-verifier job and clears the cache. */
|
|
88
|
+
public stop(): void {
|
|
89
|
+
if (this.stopped) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
this.stopped = true;
|
|
93
|
+
for (const controller of this.pending.values()) {
|
|
94
|
+
controller.abort();
|
|
95
|
+
}
|
|
96
|
+
this.pending.clear();
|
|
97
|
+
this.cache.clear();
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
export { ProvingOrchestrator } from './orchestrator.js';
|
|
2
1
|
export { CheckpointSubTreeOrchestrator, type SubTreeResult } from './checkpoint-sub-tree-orchestrator.js';
|
|
3
|
-
export {
|
|
2
|
+
export { ChonkCache, type ChonkVerifierProofResult } from './chonk-cache.js';
|
|
4
3
|
export {
|
|
5
4
|
TopTreeOrchestrator,
|
|
6
5
|
TopTreeCancelledError,
|
|
@@ -4,9 +4,9 @@ import { SerialQueue } from '@aztec/foundation/queue';
|
|
|
4
4
|
import { sleep } from '@aztec/foundation/sleep';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
* Minimal surface a deferred-proving state must expose. Both `
|
|
8
|
-
* `
|
|
9
|
-
*
|
|
7
|
+
* Minimal surface a deferred-proving state must expose. Both `CheckpointProvingState` /
|
|
8
|
+
* `BlockProvingState` (used by `CheckpointSubTreeOrchestrator`) and `TopTreeProvingState`
|
|
9
|
+
* (used by `TopTreeOrchestrator`) satisfy it.
|
|
10
10
|
*/
|
|
11
11
|
export interface ProvingStateLike {
|
|
12
12
|
/** Returns false once the state has been cancelled or otherwise invalidated. */
|
|
@@ -19,7 +19,16 @@ export interface ProvingStateLike {
|
|
|
19
19
|
* Common scheduling infrastructure shared by every orchestrator that drives broker
|
|
20
20
|
* proving jobs:
|
|
21
21
|
*
|
|
22
|
-
* -
|
|
22
|
+
* - A shared `SerialQueue` (`deferredJobQueue`) that serialises the *act of handing a
|
|
23
|
+
* job to the broker*, one initiation per event-loop tick. Each queue task kicks off
|
|
24
|
+
* `safeJob` (which submits to the broker) without awaiting it, then yields with
|
|
25
|
+
* `sleep(0)`; the next task therefore runs on the following macrotask. This does NOT
|
|
26
|
+
* cap how many broker jobs are concurrently in flight — the broker's own queue absorbs
|
|
27
|
+
* that. What it bounds is the burst rate: a sub-tree that synchronously discovers
|
|
28
|
+
* thousands of ready jobs can't flood the broker (and monopolise the event loop) in a
|
|
29
|
+
* single tick. The queue is owned by the `ProverClient` and shared across every
|
|
30
|
+
* orchestrator (every sub-tree and top-tree across every concurrent epoch session), so
|
|
31
|
+
* this pacing is applied once globally rather than once-per-orchestrator.
|
|
23
32
|
* - A list of `AbortController`s (`pendingProvingJobs`) so a `cancel()` can abort
|
|
24
33
|
* in-flight broker jobs when needed.
|
|
25
34
|
* - A `deferredProving<T>(state, request, callback, isCancelled?)` method that wraps
|
|
@@ -28,23 +37,20 @@ export interface ProvingStateLike {
|
|
|
28
37
|
*
|
|
29
38
|
* Subclasses own their own concrete proving state and define `cancelInternal()` for
|
|
30
39
|
* the rest of the cleanup work (closing world-state forks, marking sub-trees
|
|
31
|
-
* cancelled, etc.).
|
|
32
|
-
*
|
|
33
|
-
*
|
|
40
|
+
* cancelled, etc.). Because the queue is shared, neither `cancel()` nor `stop()` touch
|
|
41
|
+
* it — they only abort this orchestrator's in-flight broker jobs. Queued-but-unrun jobs
|
|
42
|
+
* for a cancelled orchestrator no-op via the guards in `deferredProving`.
|
|
34
43
|
*/
|
|
35
44
|
export abstract class ProvingScheduler {
|
|
36
45
|
protected pendingProvingJobs: AbortController[] = [];
|
|
37
46
|
protected logger: Logger;
|
|
38
|
-
private deferredJobQueue: SerialQueue;
|
|
39
47
|
|
|
40
48
|
constructor(
|
|
41
|
-
private readonly
|
|
49
|
+
private readonly deferredJobQueue: SerialQueue,
|
|
42
50
|
loggerName = 'prover-client:proving-scheduler',
|
|
43
51
|
bindings?: LoggerBindings,
|
|
44
52
|
) {
|
|
45
53
|
this.logger = createLogger(loggerName, bindings);
|
|
46
|
-
this.deferredJobQueue = new SerialQueue();
|
|
47
|
-
this.deferredJobQueue.start(this.enqueueConcurrency);
|
|
48
54
|
}
|
|
49
55
|
|
|
50
56
|
/** Number of broker jobs currently in flight. */
|
|
@@ -53,16 +59,14 @@ export abstract class ProvingScheduler {
|
|
|
53
59
|
}
|
|
54
60
|
|
|
55
61
|
/**
|
|
56
|
-
*
|
|
57
|
-
* optionally aborts every in-flight broker job. Aborting is the right choice on
|
|
62
|
+
* Optionally aborts every in-flight broker job. Aborting is the right choice on
|
|
58
63
|
* reorg-driven cancel (where the in-flight inputs are no longer valid) and the
|
|
59
64
|
* wrong choice on shutdown (where leaving jobs in the broker queue lets a restart
|
|
60
|
-
* pick them up).
|
|
65
|
+
* pick them up). The shared queue is not touched — queued-but-unrun jobs belonging
|
|
66
|
+
* to this orchestrator no-op once their controller is aborted (or once their state
|
|
67
|
+
* is marked invalid by the subclass).
|
|
61
68
|
*/
|
|
62
69
|
protected resetSchedulerState(abortJobs: boolean): void {
|
|
63
|
-
void this.deferredJobQueue.cancel();
|
|
64
|
-
this.deferredJobQueue = new SerialQueue();
|
|
65
|
-
this.deferredJobQueue.start(this.enqueueConcurrency);
|
|
66
70
|
if (abortJobs) {
|
|
67
71
|
for (const controller of this.pendingProvingJobs) {
|
|
68
72
|
controller.abort();
|
|
@@ -78,14 +82,12 @@ export abstract class ProvingScheduler {
|
|
|
78
82
|
protected abstract cancelInternal(): void;
|
|
79
83
|
|
|
80
84
|
/**
|
|
81
|
-
* Standard stop:
|
|
82
|
-
*
|
|
83
|
-
* return.
|
|
85
|
+
* Standard stop: cancel this orchestrator's work. The shared queue is owned by the
|
|
86
|
+
* `ProverClient` and outlives every orchestrator, so it is not drained here.
|
|
84
87
|
*/
|
|
85
|
-
public
|
|
86
|
-
const oldQueue = this.deferredJobQueue;
|
|
88
|
+
public stop(): Promise<void> {
|
|
87
89
|
this.cancelInternal();
|
|
88
|
-
|
|
90
|
+
return Promise.resolve();
|
|
89
91
|
}
|
|
90
92
|
|
|
91
93
|
/**
|
|
@@ -147,9 +149,11 @@ export abstract class ProvingScheduler {
|
|
|
147
149
|
};
|
|
148
150
|
|
|
149
151
|
void this.deferredJobQueue.put(async () => {
|
|
152
|
+
// Kick off the broker submission without awaiting it — awaiting here would serialise all
|
|
153
|
+
// proving (one job at a time) and kill parallelism. The `sleep(0)` yields the event loop
|
|
154
|
+
// so the next queued job initiates on the following macrotask, pacing bursts rather than
|
|
155
|
+
// bounding in-flight broker concurrency (the broker's own queue handles that).
|
|
150
156
|
void safeJob();
|
|
151
|
-
// Yield to the macrotask queue so Node has a chance to interleave other work
|
|
152
|
-
// between enqueues.
|
|
153
157
|
await sleep(0);
|
|
154
158
|
});
|
|
155
159
|
}
|
|
@@ -13,8 +13,9 @@ import { BLS12Point } from '@aztec/foundation/curves/bls12';
|
|
|
13
13
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
14
14
|
import type { LoggerBindings } from '@aztec/foundation/log';
|
|
15
15
|
import { promiseWithResolvers } from '@aztec/foundation/promise';
|
|
16
|
+
import type { SerialQueue } from '@aztec/foundation/queue';
|
|
16
17
|
import type { Tuple } from '@aztec/foundation/serialize';
|
|
17
|
-
import { MerkleTreeCalculator, shaMerkleHash } from '@aztec/foundation/trees';
|
|
18
|
+
import { MerkleTreeCalculator, type TreeNodeLocation, shaMerkleHash } from '@aztec/foundation/trees';
|
|
18
19
|
import type { EthAddress } from '@aztec/stdlib/block';
|
|
19
20
|
import type { PublicInputsAndRecursiveProof, ServerCircuitProver } from '@aztec/stdlib/interfaces/server';
|
|
20
21
|
import { computeCheckpointOutHash } from '@aztec/stdlib/messaging';
|
|
@@ -31,7 +32,7 @@ import type { BlockHeader } from '@aztec/stdlib/tx';
|
|
|
31
32
|
import { type TelemetryClient, getTelemetryClient } from '@aztec/telemetry-client';
|
|
32
33
|
|
|
33
34
|
import { buildBlobHints, toProofData } from './block-building-helpers.js';
|
|
34
|
-
import {
|
|
35
|
+
import { ProvingScheduler } from './proving-scheduler.js';
|
|
35
36
|
import { TopTreeProvingState } from './top-tree-proving-state.js';
|
|
36
37
|
|
|
37
38
|
/** Per-checkpoint data fed into the top tree. */
|
|
@@ -86,18 +87,18 @@ type OutHashHint = {
|
|
|
86
87
|
* and each checkpoint's root rollup fires the moment its sub-tree's `blockProofs`
|
|
87
88
|
* promise resolves. Later checkpoints can still be block-level proving in parallel.
|
|
88
89
|
*/
|
|
89
|
-
export class TopTreeOrchestrator extends
|
|
90
|
+
export class TopTreeOrchestrator extends ProvingScheduler {
|
|
90
91
|
private state: TopTreeProvingState | undefined;
|
|
91
92
|
private cancelled = false;
|
|
92
93
|
|
|
93
94
|
constructor(
|
|
94
|
-
prover: ServerCircuitProver,
|
|
95
|
+
protected readonly prover: ServerCircuitProver,
|
|
95
96
|
private readonly proverId: EthAddress,
|
|
96
|
-
|
|
97
|
+
deferredJobQueue: SerialQueue,
|
|
97
98
|
_telemetryClient: TelemetryClient = getTelemetryClient(),
|
|
98
99
|
bindings?: LoggerBindings,
|
|
99
100
|
) {
|
|
100
|
-
super(
|
|
101
|
+
super(deferredJobQueue, 'prover-client:top-tree-orchestrator', bindings);
|
|
101
102
|
}
|
|
102
103
|
|
|
103
104
|
public getProverId(): EthAddress {
|
|
@@ -133,10 +134,9 @@ export class TopTreeOrchestrator extends TopTreeProvingScheduler {
|
|
|
133
134
|
}
|
|
134
135
|
|
|
135
136
|
const { promise: completionPromise, resolve, reject } = promiseWithResolvers<void>();
|
|
136
|
-
// The completion promise is awaited
|
|
137
|
-
//
|
|
138
|
-
//
|
|
139
|
-
// is silenced.
|
|
137
|
+
// The completion promise is awaited below. Attach a no-op catch here as well so any
|
|
138
|
+
// spurious unhandled-rejection detection during cancellation (where reject() can fire
|
|
139
|
+
// synchronously before the await microtask installs a handler) is silenced.
|
|
140
140
|
completionPromise.catch(() => {});
|
|
141
141
|
const startBlobAccumulator = BatchedBlobAccumulator.newWithChallenges(finalBlobBatchingChallenges);
|
|
142
142
|
|
|
@@ -189,16 +189,13 @@ export class TopTreeOrchestrator extends TopTreeProvingScheduler {
|
|
|
189
189
|
);
|
|
190
190
|
}
|
|
191
191
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
}
|
|
200
|
-
throw err;
|
|
201
|
-
}
|
|
192
|
+
// The error type is stamped atomically at rejection time by the rejectionCallback above
|
|
193
|
+
// (TopTreeCancelledError iff cancel() drove the rejection). Re-deriving it here from the
|
|
194
|
+
// live `cancelled` flag would mask a genuine failure that lost a race with a late cancel
|
|
195
|
+
// (A-1035), so let the original error propagate untouched.
|
|
196
|
+
await completionPromise;
|
|
197
|
+
await this.state.finalizeBatchedBlob();
|
|
198
|
+
return this.state.getEpochProofResult();
|
|
202
199
|
}
|
|
203
200
|
|
|
204
201
|
/**
|
|
@@ -220,10 +217,6 @@ export class TopTreeOrchestrator extends TopTreeProvingScheduler {
|
|
|
220
217
|
|
|
221
218
|
// --- internal: per-checkpoint enqueue path ---
|
|
222
219
|
|
|
223
|
-
protected override onRootRollupComplete(state: TopTreeProvingState) {
|
|
224
|
-
state.resolve();
|
|
225
|
-
}
|
|
226
|
-
|
|
227
220
|
private enqueueCheckpointRoot(
|
|
228
221
|
state: TopTreeProvingState,
|
|
229
222
|
checkpointIndex: number,
|
|
@@ -235,26 +228,36 @@ export class TopTreeOrchestrator extends TopTreeProvingScheduler {
|
|
|
235
228
|
outHashHint: OutHashHint,
|
|
236
229
|
startBlobAccumulator: BatchedBlobAccumulator,
|
|
237
230
|
) {
|
|
238
|
-
void this.buildCheckpointRootInputs(blockProofs, cd, outHashHint, startBlobAccumulator).then(
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
231
|
+
void this.buildCheckpointRootInputs(blockProofs, cd, outHashHint, startBlobAccumulator).then(
|
|
232
|
+
inputs => {
|
|
233
|
+
this.deferredProving(
|
|
234
|
+
state,
|
|
235
|
+
signal => {
|
|
236
|
+
if (inputs instanceof CheckpointRootSingleBlockRollupPrivateInputs) {
|
|
237
|
+
return this.prover.getCheckpointRootSingleBlockRollupProof(inputs, signal, state.epochNumber);
|
|
238
|
+
}
|
|
239
|
+
return this.prover.getCheckpointRootRollupProof(inputs, signal, state.epochNumber);
|
|
240
|
+
},
|
|
241
|
+
result => {
|
|
242
|
+
this.logger.debug(`Completed checkpoint root proof for checkpoint ${checkpointIndex}`);
|
|
243
|
+
const leafLocation = state.setCheckpointRootRollupProof(checkpointIndex, result);
|
|
244
|
+
if (state.totalNumCheckpoints === 1) {
|
|
245
|
+
this.enqueueEpochPadding(state);
|
|
246
|
+
} else {
|
|
247
|
+
this.checkAndEnqueueNextCheckpointMergeRollup(state, leafLocation);
|
|
248
|
+
}
|
|
249
|
+
},
|
|
250
|
+
);
|
|
251
|
+
},
|
|
252
|
+
// Without this, an input-building failure rejects a discarded promise, state.reject() is
|
|
253
|
+
// never called, and prove() hangs forever on its completion promise (A-1036).
|
|
254
|
+
err => {
|
|
255
|
+
if (this.cancelled) {
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
state.reject(`Building checkpoint root inputs for checkpoint ${checkpointIndex} failed: ${err}`);
|
|
259
|
+
},
|
|
260
|
+
);
|
|
258
261
|
}
|
|
259
262
|
|
|
260
263
|
private async buildCheckpointRootInputs(
|
|
@@ -286,6 +289,73 @@ export class TopTreeOrchestrator extends TopTreeProvingScheduler {
|
|
|
286
289
|
: new CheckpointRootRollupPrivateInputs([proofDatas[0], proofDatas[1]], hints);
|
|
287
290
|
}
|
|
288
291
|
|
|
292
|
+
// --- internal: top-tree proof orchestration (formerly TopTreeProvingScheduler) ---
|
|
293
|
+
|
|
294
|
+
private enqueueCheckpointMergeRollup(state: TopTreeProvingState, location: TreeNodeLocation) {
|
|
295
|
+
if (!state.verifyState() || !state.tryStartProvingCheckpointMerge(location)) {
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
const inputs = state.getCheckpointMergeRollupInputs(location);
|
|
299
|
+
this.deferredProving(
|
|
300
|
+
state,
|
|
301
|
+
signal => this.prover.getCheckpointMergeRollupProof(inputs, signal, state.epochNumber),
|
|
302
|
+
result => {
|
|
303
|
+
state.setCheckpointMergeRollupProof(location, result);
|
|
304
|
+
this.checkAndEnqueueNextCheckpointMergeRollup(state, location);
|
|
305
|
+
},
|
|
306
|
+
);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
private enqueueEpochPadding(state: TopTreeProvingState) {
|
|
310
|
+
if (!state.verifyState() || !state.tryStartProvingPaddingCheckpoint()) {
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
const inputs = state.getPaddingCheckpointInputs();
|
|
314
|
+
this.deferredProving(
|
|
315
|
+
state,
|
|
316
|
+
signal => this.prover.getCheckpointPaddingRollupProof(inputs, signal, state.epochNumber),
|
|
317
|
+
result => {
|
|
318
|
+
state.setCheckpointPaddingProof(result);
|
|
319
|
+
this.checkAndEnqueueRootRollup(state);
|
|
320
|
+
},
|
|
321
|
+
);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
private enqueueRootRollup(state: TopTreeProvingState) {
|
|
325
|
+
if (!state.verifyState() || !state.tryStartProvingRootRollup()) {
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
const inputs = state.getRootRollupInputs();
|
|
329
|
+
this.deferredProving(
|
|
330
|
+
state,
|
|
331
|
+
signal => this.prover.getRootRollupProof(inputs, signal, state.epochNumber),
|
|
332
|
+
result => {
|
|
333
|
+
this.logger.verbose(`Completed root rollup for epoch ${state.epochNumber}`);
|
|
334
|
+
state.setRootRollupProof(result);
|
|
335
|
+
state.resolve();
|
|
336
|
+
},
|
|
337
|
+
);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
private checkAndEnqueueNextCheckpointMergeRollup(state: TopTreeProvingState, currentLocation: TreeNodeLocation) {
|
|
341
|
+
if (!state.isReadyForCheckpointMerge(currentLocation)) {
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
const parentLocation = state.getParentLocation(currentLocation);
|
|
345
|
+
if (parentLocation.level === 0) {
|
|
346
|
+
this.checkAndEnqueueRootRollup(state);
|
|
347
|
+
} else {
|
|
348
|
+
this.enqueueCheckpointMergeRollup(state, parentLocation);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
private checkAndEnqueueRootRollup(state: TopTreeProvingState) {
|
|
353
|
+
if (!state.isReadyForRootRollup()) {
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
this.enqueueRootRollup(state);
|
|
357
|
+
}
|
|
358
|
+
|
|
289
359
|
private async computeOutHashHints(checkpointData: CheckpointTopTreeData[]): Promise<OutHashHint[]> {
|
|
290
360
|
const treeCalculator = await MerkleTreeCalculator.create(OUT_HASH_TREE_HEIGHT, undefined, (left, right) =>
|
|
291
361
|
Promise.resolve(shaMerkleHash(left, right)),
|
|
@@ -41,7 +41,6 @@ export class TopTreeProvingState {
|
|
|
41
41
|
private endBlobAccumulator: BatchedBlobAccumulator | undefined;
|
|
42
42
|
private finalBatchedBlob: BatchedBlob | undefined;
|
|
43
43
|
private lifecycle = TOP_TREE_LIFECYCLE.CREATED;
|
|
44
|
-
|
|
45
44
|
constructor(
|
|
46
45
|
public readonly epochNumber: EpochNumber,
|
|
47
46
|
public readonly totalNumCheckpoints: number,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AVM_V2_PROOF_LENGTH_IN_FIELDS, NESTED_RECURSIVE_ROLLUP_HONK_PROOF_LENGTH } from '@aztec/constants';
|
|
2
2
|
import type { Fr } from '@aztec/foundation/curves/bn254';
|
|
3
3
|
import { getVkData } from '@aztec/noir-protocol-circuits-types/server/vks';
|
|
4
4
|
import type { AvmCircuitInputs } from '@aztec/stdlib/avm';
|
|
@@ -32,7 +32,7 @@ export class TxProvingState {
|
|
|
32
32
|
PublicChonkVerifierPublicInputs,
|
|
33
33
|
typeof NESTED_RECURSIVE_ROLLUP_HONK_PROOF_LENGTH
|
|
34
34
|
>;
|
|
35
|
-
private avmProof?: RecursiveProof<typeof
|
|
35
|
+
private avmProof?: RecursiveProof<typeof AVM_V2_PROOF_LENGTH_IN_FIELDS>;
|
|
36
36
|
|
|
37
37
|
constructor(
|
|
38
38
|
public readonly processedTx: ProcessedTx,
|
|
@@ -80,7 +80,7 @@ export class TxProvingState {
|
|
|
80
80
|
this.publicChonkVerifier = publicChonkVerifierProofAndVk;
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
public setAvmProof(avmProof: RecursiveProof<typeof
|
|
83
|
+
public setAvmProof(avmProof: RecursiveProof<typeof AVM_V2_PROOF_LENGTH_IN_FIELDS>) {
|
|
84
84
|
this.avmProof = avmProof;
|
|
85
85
|
}
|
|
86
86
|
|
|
@@ -4,10 +4,10 @@ import { times } from '@aztec/foundation/collection';
|
|
|
4
4
|
import type { Fr } from '@aztec/foundation/curves/bn254';
|
|
5
5
|
import type { EthAddress } from '@aztec/foundation/eth-address';
|
|
6
6
|
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
7
|
+
import { SerialQueue } from '@aztec/foundation/queue';
|
|
7
8
|
import { NativeACVMSimulator } from '@aztec/simulator/server';
|
|
8
9
|
import {
|
|
9
10
|
type ActualProverConfig,
|
|
10
|
-
type EpochProver,
|
|
11
11
|
type EpochProverManager,
|
|
12
12
|
type ForkMerkleTreeOperations,
|
|
13
13
|
type ProvingJobBroker,
|
|
@@ -23,13 +23,11 @@ import { type TelemetryClient, getTelemetryClient } from '@aztec/telemetry-clien
|
|
|
23
23
|
|
|
24
24
|
import type { ProverClientConfig } from '../config.js';
|
|
25
25
|
import { CheckpointSubTreeOrchestrator } from '../orchestrator/checkpoint-sub-tree-orchestrator.js';
|
|
26
|
-
import {
|
|
27
|
-
import { ProvingOrchestrator } from '../orchestrator/orchestrator.js';
|
|
26
|
+
import type { ChonkCache } from '../orchestrator/chonk-cache.js';
|
|
28
27
|
import { TopTreeOrchestrator } from '../orchestrator/top-tree-orchestrator.js';
|
|
29
28
|
import { BrokerCircuitProverFacade } from '../proving_broker/broker_prover_facade.js';
|
|
30
29
|
import { InlineProofStore, type ProofStore, createProofStore } from '../proving_broker/proof_store/index.js';
|
|
31
30
|
import { ProvingAgent } from '../proving_broker/proving_agent.js';
|
|
32
|
-
import { ServerEpochProver } from './server-epoch-prover.js';
|
|
33
31
|
|
|
34
32
|
/**
|
|
35
33
|
* The factory surface that `EpochProvingJob` (in `prover-node`) depends on. Implemented
|
|
@@ -49,14 +47,13 @@ import { ServerEpochProver } from './server-epoch-prover.js';
|
|
|
49
47
|
export interface EpochProverFactory {
|
|
50
48
|
getProverId(): EthAddress;
|
|
51
49
|
/**
|
|
52
|
-
* Constructs
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Constructs and starts a `CheckpointSubTreeOrchestrator` for a single checkpoint.
|
|
50
|
+
* Constructs and starts a `CheckpointSubTreeOrchestrator` for a single checkpoint
|
|
51
|
+
* against the supplied shared `chonkCache`. The cache is owned by the prover-node
|
|
52
|
+
* and survives across epochs / sessions.
|
|
57
53
|
*/
|
|
58
54
|
createCheckpointSubTreeOrchestrator(
|
|
59
|
-
|
|
55
|
+
chonkCache: ChonkCache,
|
|
56
|
+
epochNumber: EpochNumber,
|
|
60
57
|
checkpointConstants: CheckpointConstantData,
|
|
61
58
|
l1ToL2Messages: Fr[],
|
|
62
59
|
totalNumBlocks: number,
|
|
@@ -75,6 +72,13 @@ export class ProverClient implements EpochProverManager, EpochProverFactory {
|
|
|
75
72
|
* `EpochProverFactory` for why a single shared facade is required.
|
|
76
73
|
*/
|
|
77
74
|
private facade: BrokerCircuitProverFacade | undefined;
|
|
75
|
+
/**
|
|
76
|
+
* Single deferred-proving-job queue shared across every orchestrator (sub-trees and
|
|
77
|
+
* top-trees, across every concurrent epoch session). Throttles the total rate of job
|
|
78
|
+
* submission to the broker once, rather than once per orchestrator. Started lazily
|
|
79
|
+
* alongside the facade and cancelled on `stop()`.
|
|
80
|
+
*/
|
|
81
|
+
private deferredJobQueue: SerialQueue | undefined;
|
|
78
82
|
|
|
79
83
|
private constructor(
|
|
80
84
|
private config: ProverClientConfig,
|
|
@@ -112,40 +116,21 @@ export class ProverClient implements EpochProverManager, EpochProverFactory {
|
|
|
112
116
|
return this.facade;
|
|
113
117
|
}
|
|
114
118
|
|
|
115
|
-
/**
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
this.orchestratorClient,
|
|
126
|
-
this.proofStore,
|
|
127
|
-
this.failedProofStore,
|
|
128
|
-
undefined,
|
|
129
|
-
bindings,
|
|
130
|
-
);
|
|
131
|
-
const orchestrator = new ProvingOrchestrator(
|
|
132
|
-
this.worldState,
|
|
133
|
-
facade,
|
|
134
|
-
this.config.proverId,
|
|
135
|
-
this.config.cancelJobsOnStop,
|
|
136
|
-
this.config.enqueueConcurrency,
|
|
137
|
-
this.telemetry,
|
|
138
|
-
bindings,
|
|
139
|
-
);
|
|
140
|
-
return new ServerEpochProver(facade, orchestrator);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
public createEpochProvingContext(epochNumber: EpochNumber): EpochProvingContext {
|
|
144
|
-
return new EpochProvingContext(this.getFacade(), epochNumber, this.log.getBindings());
|
|
119
|
+
/** Lazy-init the shared deferred-job queue, started with the configured enqueue concurrency. */
|
|
120
|
+
private getDeferredJobQueue(): SerialQueue {
|
|
121
|
+
if (!this.running) {
|
|
122
|
+
throw new Error('ProverClient is not running; call start() before constructing orchestrators.');
|
|
123
|
+
}
|
|
124
|
+
if (!this.deferredJobQueue) {
|
|
125
|
+
this.deferredJobQueue = new SerialQueue();
|
|
126
|
+
this.deferredJobQueue.start(this.config.enqueueConcurrency);
|
|
127
|
+
}
|
|
128
|
+
return this.deferredJobQueue;
|
|
145
129
|
}
|
|
146
130
|
|
|
147
131
|
public createCheckpointSubTreeOrchestrator(
|
|
148
|
-
|
|
132
|
+
chonkCache: ChonkCache,
|
|
133
|
+
epochNumber: EpochNumber,
|
|
149
134
|
checkpointConstants: CheckpointConstantData,
|
|
150
135
|
l1ToL2Messages: Fr[],
|
|
151
136
|
totalNumBlocks: number,
|
|
@@ -155,9 +140,10 @@ export class ProverClient implements EpochProverManager, EpochProverFactory {
|
|
|
155
140
|
this.worldState,
|
|
156
141
|
this.getFacade(),
|
|
157
142
|
this.config.proverId,
|
|
158
|
-
|
|
143
|
+
chonkCache,
|
|
144
|
+
epochNumber,
|
|
159
145
|
this.config.cancelJobsOnStop,
|
|
160
|
-
this.
|
|
146
|
+
this.getDeferredJobQueue(),
|
|
161
147
|
checkpointConstants,
|
|
162
148
|
l1ToL2Messages,
|
|
163
149
|
totalNumBlocks,
|
|
@@ -171,7 +157,7 @@ export class ProverClient implements EpochProverManager, EpochProverFactory {
|
|
|
171
157
|
return new TopTreeOrchestrator(
|
|
172
158
|
this.getFacade(),
|
|
173
159
|
this.config.proverId,
|
|
174
|
-
this.
|
|
160
|
+
this.getDeferredJobQueue(),
|
|
175
161
|
this.telemetry,
|
|
176
162
|
this.log.getBindings(),
|
|
177
163
|
);
|
|
@@ -216,12 +202,12 @@ export class ProverClient implements EpochProverManager, EpochProverFactory {
|
|
|
216
202
|
}
|
|
217
203
|
this.running = false;
|
|
218
204
|
await this.stopAgents();
|
|
205
|
+
if (this.deferredJobQueue) {
|
|
206
|
+
await this.deferredJobQueue.cancel();
|
|
207
|
+
this.deferredJobQueue = undefined;
|
|
208
|
+
}
|
|
219
209
|
if (this.facade) {
|
|
220
|
-
|
|
221
|
-
await this.facade.stop();
|
|
222
|
-
} catch (err) {
|
|
223
|
-
this.log.error('Error stopping shared broker facade', err);
|
|
224
|
-
}
|
|
210
|
+
await tryStop(this.facade, this.log);
|
|
225
211
|
this.facade = undefined;
|
|
226
212
|
}
|
|
227
213
|
await tryStop(this.orchestratorClient);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type {
|
|
2
|
-
|
|
2
|
+
AVM_V2_PROOF_LENGTH_IN_FIELDS,
|
|
3
3
|
NESTED_RECURSIVE_PROOF_LENGTH,
|
|
4
4
|
NESTED_RECURSIVE_ROLLUP_HONK_PROOF_LENGTH,
|
|
5
5
|
RECURSIVE_PROOF_LENGTH,
|
|
@@ -394,7 +394,7 @@ export class BrokerCircuitProverFacade implements ServerCircuitProver {
|
|
|
394
394
|
inputs: AvmCircuitInputs,
|
|
395
395
|
signal?: AbortSignal,
|
|
396
396
|
epochNumber?: EpochNumber,
|
|
397
|
-
): Promise<RecursiveProof<typeof
|
|
397
|
+
): Promise<RecursiveProof<typeof AVM_V2_PROOF_LENGTH_IN_FIELDS>> {
|
|
398
398
|
return this.enqueueJob(
|
|
399
399
|
this.generateId(ProvingRequestType.PUBLIC_VM, inputs, epochNumber),
|
|
400
400
|
ProvingRequestType.PUBLIC_VM,
|