@aztec/prover-client 0.0.1-commit.e6bd8901 → 0.0.1-commit.ec7ac5448
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/config.d.ts +1 -1
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +12 -2
- package/dest/light/lightweight_checkpoint_builder.d.ts +13 -5
- package/dest/light/lightweight_checkpoint_builder.d.ts.map +1 -1
- package/dest/light/lightweight_checkpoint_builder.js +49 -20
- package/dest/mocks/fixtures.d.ts +1 -1
- package/dest/mocks/fixtures.d.ts.map +1 -1
- package/dest/mocks/fixtures.js +2 -1
- package/dest/mocks/test_context.d.ts +1 -1
- package/dest/mocks/test_context.d.ts.map +1 -1
- package/dest/mocks/test_context.js +17 -9
- package/dest/orchestrator/block-building-helpers.d.ts +4 -4
- package/dest/orchestrator/block-building-helpers.d.ts.map +1 -1
- package/dest/orchestrator/block-building-helpers.js +3 -3
- package/dest/orchestrator/block-proving-state.d.ts +4 -1
- package/dest/orchestrator/block-proving-state.d.ts.map +1 -1
- package/dest/orchestrator/block-proving-state.js +7 -0
- package/dest/orchestrator/checkpoint-proving-state.d.ts +3 -3
- package/dest/orchestrator/checkpoint-proving-state.d.ts.map +1 -1
- package/dest/orchestrator/checkpoint-proving-state.js +3 -3
- package/dest/orchestrator/epoch-proving-state.d.ts +3 -3
- package/dest/orchestrator/epoch-proving-state.d.ts.map +1 -1
- package/dest/orchestrator/epoch-proving-state.js +5 -3
- package/dest/orchestrator/orchestrator.d.ts +7 -3
- package/dest/orchestrator/orchestrator.d.ts.map +1 -1
- package/dest/orchestrator/orchestrator.js +125 -117
- package/dest/prover-client/prover-client.d.ts +4 -4
- package/dest/prover-client/prover-client.d.ts.map +1 -1
- package/dest/prover-client/prover-client.js +15 -10
- package/dest/proving_broker/broker_prover_facade.d.ts +4 -3
- package/dest/proving_broker/broker_prover_facade.d.ts.map +1 -1
- package/dest/proving_broker/broker_prover_facade.js +16 -22
- package/dest/proving_broker/config.d.ts +10 -2
- package/dest/proving_broker/config.d.ts.map +1 -1
- package/dest/proving_broker/config.js +14 -3
- package/dest/proving_broker/proof_store/factory.d.ts +2 -5
- package/dest/proving_broker/proof_store/factory.d.ts.map +1 -1
- package/dest/proving_broker/proof_store/factory.js +7 -30
- package/dest/proving_broker/proof_store/file_store_proof_store.d.ts +18 -0
- package/dest/proving_broker/proof_store/file_store_proof_store.d.ts.map +1 -0
- package/dest/proving_broker/proof_store/file_store_proof_store.js +60 -0
- package/dest/proving_broker/proof_store/index.d.ts +2 -2
- package/dest/proving_broker/proof_store/index.d.ts.map +1 -1
- package/dest/proving_broker/proof_store/index.js +1 -1
- package/dest/proving_broker/proving_agent.d.ts +4 -3
- package/dest/proving_broker/proving_agent.d.ts.map +1 -1
- package/dest/proving_broker/proving_agent.js +4 -4
- package/dest/proving_broker/proving_broker.d.ts +7 -4
- package/dest/proving_broker/proving_broker.d.ts.map +1 -1
- package/dest/proving_broker/proving_broker.js +56 -8
- package/dest/proving_broker/proving_broker_instrumentation.d.ts +3 -1
- package/dest/proving_broker/proving_broker_instrumentation.d.ts.map +1 -1
- package/dest/proving_broker/proving_broker_instrumentation.js +18 -7
- package/dest/proving_broker/proving_job_controller.d.ts +4 -3
- package/dest/proving_broker/proving_job_controller.d.ts.map +1 -1
- package/dest/proving_broker/proving_job_controller.js +6 -3
- package/dest/proving_broker/rpc.d.ts +6 -2
- package/dest/proving_broker/rpc.d.ts.map +1 -1
- package/dest/proving_broker/rpc.js +24 -15
- package/dest/test/mock_prover.d.ts +4 -4
- package/package.json +17 -18
- package/src/config.ts +13 -2
- package/src/light/lightweight_checkpoint_builder.ts +59 -21
- package/src/mocks/fixtures.ts +2 -1
- package/src/mocks/test_context.ts +12 -10
- package/src/orchestrator/block-building-helpers.ts +3 -3
- package/src/orchestrator/block-proving-state.ts +9 -0
- package/src/orchestrator/checkpoint-proving-state.ts +4 -4
- package/src/orchestrator/epoch-proving-state.ts +6 -4
- package/src/orchestrator/orchestrator.ts +135 -125
- package/src/prover-client/prover-client.ts +24 -14
- package/src/proving_broker/broker_prover_facade.ts +23 -23
- package/src/proving_broker/config.ts +14 -1
- package/src/proving_broker/proof_store/factory.ts +10 -32
- package/src/proving_broker/proof_store/file_store_proof_store.ts +78 -0
- package/src/proving_broker/proof_store/index.ts +1 -1
- package/src/proving_broker/proving_agent.ts +5 -2
- package/src/proving_broker/proving_broker.ts +55 -5
- package/src/proving_broker/proving_broker_instrumentation.ts +19 -6
- package/src/proving_broker/proving_job_controller.ts +9 -3
- package/src/proving_broker/rpc.ts +26 -3
- package/dest/proving_broker/proof_store/gcs_proof_store.d.ts +0 -14
- package/dest/proving_broker/proof_store/gcs_proof_store.d.ts.map +0 -1
- package/dest/proving_broker/proof_store/gcs_proof_store.js +0 -52
- package/src/proving_broker/proof_store/gcs_proof_store.ts +0 -76
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type ACVMConfig, type BBConfig, BBNativeRollupProver, TestCircuitProver } from '@aztec/bb-prover';
|
|
2
2
|
import { times } from '@aztec/foundation/collection';
|
|
3
3
|
import type { EthAddress } from '@aztec/foundation/eth-address';
|
|
4
|
-
import { createLogger } from '@aztec/foundation/log';
|
|
4
|
+
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
5
5
|
import { NativeACVMSimulator } from '@aztec/simulator/server';
|
|
6
6
|
import {
|
|
7
7
|
type ActualProverConfig,
|
|
@@ -29,29 +29,34 @@ export class ProverClient implements EpochProverManager {
|
|
|
29
29
|
private running = false;
|
|
30
30
|
private agents: ProvingAgent[] = [];
|
|
31
31
|
|
|
32
|
-
private proofStore: ProofStore;
|
|
33
|
-
private failedProofStore: ProofStore | undefined;
|
|
34
|
-
|
|
35
32
|
private constructor(
|
|
36
33
|
private config: ProverClientConfig,
|
|
37
34
|
private worldState: ForkMerkleTreeOperations & ReadonlyWorldStateAccess,
|
|
38
35
|
private orchestratorClient: ProvingJobProducer,
|
|
36
|
+
private proofStore: ProofStore,
|
|
37
|
+
private failedProofStore: ProofStore | undefined,
|
|
39
38
|
private agentClient?: ProvingJobConsumer,
|
|
40
39
|
private telemetry: TelemetryClient = getTelemetryClient(),
|
|
41
|
-
private log = createLogger('prover-client:tx-prover'),
|
|
42
|
-
) {
|
|
43
|
-
this.proofStore = new InlineProofStore();
|
|
44
|
-
this.failedProofStore = this.config.failedProofStore ? createProofStore(this.config.failedProofStore) : undefined;
|
|
45
|
-
}
|
|
40
|
+
private log: Logger = createLogger('prover-client:tx-prover'),
|
|
41
|
+
) {}
|
|
46
42
|
|
|
47
43
|
public createEpochProver(): EpochProver {
|
|
48
|
-
const
|
|
44
|
+
const bindings = this.log.getBindings();
|
|
45
|
+
const facade = new BrokerCircuitProverFacade(
|
|
46
|
+
this.orchestratorClient,
|
|
47
|
+
this.proofStore,
|
|
48
|
+
this.failedProofStore,
|
|
49
|
+
undefined,
|
|
50
|
+
bindings,
|
|
51
|
+
);
|
|
49
52
|
const orchestrator = new ProvingOrchestrator(
|
|
50
53
|
this.worldState,
|
|
51
54
|
facade,
|
|
52
55
|
this.config.proverId,
|
|
53
56
|
this.config.cancelJobsOnStop,
|
|
57
|
+
this.config.enqueueConcurrency,
|
|
54
58
|
this.telemetry,
|
|
59
|
+
bindings,
|
|
55
60
|
);
|
|
56
61
|
return new ServerEpochProver(facade, orchestrator);
|
|
57
62
|
}
|
|
@@ -110,7 +115,9 @@ export class ProverClient implements EpochProverManager {
|
|
|
110
115
|
broker: ProvingJobBroker,
|
|
111
116
|
telemetry: TelemetryClient = getTelemetryClient(),
|
|
112
117
|
) {
|
|
113
|
-
const
|
|
118
|
+
const proofStore = await createProofStore(config.proofStore);
|
|
119
|
+
const failedProofStore = config.failedProofStore ? await createProofStore(config.failedProofStore) : undefined;
|
|
120
|
+
const prover = new ProverClient(config, worldState, broker, proofStore, failedProofStore, broker, telemetry);
|
|
114
121
|
await prover.start();
|
|
115
122
|
return prover;
|
|
116
123
|
}
|
|
@@ -134,9 +141,11 @@ export class ProverClient implements EpochProverManager {
|
|
|
134
141
|
|
|
135
142
|
const proofStore = new InlineProofStore();
|
|
136
143
|
const prover = await buildServerCircuitProver(this.config, this.telemetry);
|
|
144
|
+
const bindings = this.log.getBindings();
|
|
137
145
|
this.agents = times(
|
|
138
146
|
this.config.proverAgentCount,
|
|
139
|
-
() =>
|
|
147
|
+
() =>
|
|
148
|
+
new ProvingAgent(this.agentClient!, proofStore, prover, [], this.config.proverAgentPollIntervalMs, bindings),
|
|
140
149
|
);
|
|
141
150
|
|
|
142
151
|
await Promise.all(this.agents.map(agent => agent.start()));
|
|
@@ -148,15 +157,16 @@ export class ProverClient implements EpochProverManager {
|
|
|
148
157
|
}
|
|
149
158
|
|
|
150
159
|
export function buildServerCircuitProver(
|
|
151
|
-
config: ActualProverConfig & ACVMConfig & BBConfig,
|
|
160
|
+
config: Omit<ActualProverConfig, 'enqueueConcurrency'> & ACVMConfig & BBConfig,
|
|
152
161
|
telemetry: TelemetryClient,
|
|
153
162
|
): Promise<ServerCircuitProver> {
|
|
154
163
|
if (config.realProofs) {
|
|
155
164
|
return BBNativeRollupProver.new(config, telemetry);
|
|
156
165
|
}
|
|
157
166
|
|
|
167
|
+
const logger = createLogger('prover-client:acvm-native');
|
|
158
168
|
const simulator = config.acvmBinaryPath
|
|
159
|
-
? new NativeACVMSimulator(config.acvmWorkingDirectory, config.acvmBinaryPath)
|
|
169
|
+
? new NativeACVMSimulator(config.acvmWorkingDirectory, config.acvmBinaryPath, undefined, logger)
|
|
160
170
|
: undefined;
|
|
161
171
|
|
|
162
172
|
return Promise.resolve(new TestCircuitProver(simulator, config, telemetry));
|
|
@@ -4,9 +4,10 @@ import type {
|
|
|
4
4
|
NESTED_RECURSIVE_ROLLUP_HONK_PROOF_LENGTH,
|
|
5
5
|
RECURSIVE_PROOF_LENGTH,
|
|
6
6
|
} from '@aztec/constants';
|
|
7
|
+
import { asyncPool } from '@aztec/foundation/async-pool';
|
|
7
8
|
import { EpochNumber } from '@aztec/foundation/branded-types';
|
|
8
|
-
import {
|
|
9
|
-
import { createLogger } from '@aztec/foundation/log';
|
|
9
|
+
import { chunk } from '@aztec/foundation/collection';
|
|
10
|
+
import { type Logger, type LoggerBindings, createLogger } from '@aztec/foundation/log';
|
|
10
11
|
import { type PromiseWithResolvers, RunningPromise, promiseWithResolvers } from '@aztec/foundation/promise';
|
|
11
12
|
import { truncate } from '@aztec/foundation/string';
|
|
12
13
|
import type { AvmCircuitInputs } from '@aztec/stdlib/avm';
|
|
@@ -46,6 +47,8 @@ import type {
|
|
|
46
47
|
TxRollupPublicInputs,
|
|
47
48
|
} from '@aztec/stdlib/rollup';
|
|
48
49
|
|
|
50
|
+
import { createHash } from 'node:crypto';
|
|
51
|
+
|
|
49
52
|
import { InlineProofStore, type ProofStore } from './proof_store/index.js';
|
|
50
53
|
|
|
51
54
|
// Perform a snapshot sync every 30 seconds
|
|
@@ -68,14 +71,17 @@ export class BrokerCircuitProverFacade implements ServerCircuitProver {
|
|
|
68
71
|
private runningPromise?: RunningPromise;
|
|
69
72
|
private timeOfLastSnapshotSync = Date.now();
|
|
70
73
|
private jobsToRetrieve: Set<ProvingJobId> = new Set();
|
|
74
|
+
private log: Logger;
|
|
71
75
|
|
|
72
76
|
constructor(
|
|
73
77
|
private broker: ProvingJobProducer,
|
|
74
78
|
private proofStore: ProofStore = new InlineProofStore(),
|
|
75
79
|
private failedProofStore?: ProofStore,
|
|
76
80
|
private pollIntervalMs = 1000,
|
|
77
|
-
|
|
78
|
-
) {
|
|
81
|
+
bindings?: LoggerBindings,
|
|
82
|
+
) {
|
|
83
|
+
this.log = createLogger('prover-client:broker-circuit-prover-facade', bindings);
|
|
84
|
+
}
|
|
79
85
|
|
|
80
86
|
/**
|
|
81
87
|
* This is a critical section. This function can not be async since it writes
|
|
@@ -222,17 +228,11 @@ export class BrokerCircuitProverFacade implements ServerCircuitProver {
|
|
|
222
228
|
// We collect all returned notifications and return them
|
|
223
229
|
const allCompleted = new Set<ProvingJobId>();
|
|
224
230
|
try {
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
const
|
|
228
|
-
const completed = await this.broker.getCompletedJobs(slice);
|
|
231
|
+
const batches = ids.length > 0 ? chunk(ids, SNAPSHOT_SYNC_CHECK_MAX_REQUEST_SIZE) : [[]];
|
|
232
|
+
await asyncPool(1, batches, async batch => {
|
|
233
|
+
const completed = await this.broker.getCompletedJobs(batch);
|
|
229
234
|
completed.forEach(id => allCompleted.add(id));
|
|
230
|
-
|
|
231
|
-
}
|
|
232
|
-
if (numRequests === 0) {
|
|
233
|
-
const final = await this.broker.getCompletedJobs([]);
|
|
234
|
-
final.forEach(id => allCompleted.add(id));
|
|
235
|
-
}
|
|
235
|
+
});
|
|
236
236
|
} catch (err) {
|
|
237
237
|
this.log.error(`Error thrown when requesting completed job notifications from the broker`, err);
|
|
238
238
|
}
|
|
@@ -348,12 +348,8 @@ export class BrokerCircuitProverFacade implements ServerCircuitProver {
|
|
|
348
348
|
.map(id => this.jobs.get(id)!)
|
|
349
349
|
.filter(x => x !== undefined);
|
|
350
350
|
const totalJobsToRetrieve = toBeRetrieved.length;
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
const slice = toBeRetrieved.splice(0, MAX_CONCURRENT_JOB_SETTLED_REQUESTS);
|
|
354
|
-
const results = await Promise.all(slice.map(job => processJob(job!)));
|
|
355
|
-
totalJobsRetrieved += results.filter(x => x).length;
|
|
356
|
-
}
|
|
351
|
+
const results = await asyncPool(MAX_CONCURRENT_JOB_SETTLED_REQUESTS, toBeRetrieved, job => processJob(job));
|
|
352
|
+
const totalJobsRetrieved = results.filter(x => x).length;
|
|
357
353
|
if (totalJobsToRetrieve > 0) {
|
|
358
354
|
this.log.verbose(
|
|
359
355
|
`Successfully retrieved ${totalJobsRetrieved} of ${totalJobsToRetrieve} jobs that should be ready, total ready jobs is now: ${this.jobsToRetrieve.size}`,
|
|
@@ -656,8 +652,12 @@ export class BrokerCircuitProverFacade implements ServerCircuitProver {
|
|
|
656
652
|
);
|
|
657
653
|
}
|
|
658
654
|
|
|
659
|
-
private generateId(
|
|
660
|
-
|
|
661
|
-
|
|
655
|
+
private generateId(
|
|
656
|
+
type: ProvingRequestType,
|
|
657
|
+
inputs: { toBuffer(): Buffer },
|
|
658
|
+
epochNumber = EpochNumber.ZERO,
|
|
659
|
+
): ProvingJobId {
|
|
660
|
+
const inputsHash = createHash('sha256').update(inputs.toBuffer()).digest('hex');
|
|
661
|
+
return makeProvingJobId(epochNumber, type, inputsHash);
|
|
662
662
|
}
|
|
663
663
|
}
|
|
@@ -6,8 +6,8 @@ import {
|
|
|
6
6
|
numberConfigHelper,
|
|
7
7
|
} from '@aztec/foundation/config';
|
|
8
8
|
import { pickConfigMappings } from '@aztec/foundation/config';
|
|
9
|
-
import { type DataStoreConfig, dataConfigMappings } from '@aztec/kv-store/config';
|
|
10
9
|
import { type ChainConfig, chainConfigMappings } from '@aztec/stdlib/config';
|
|
10
|
+
import { type DataStoreConfig, dataConfigMappings } from '@aztec/stdlib/kv-store';
|
|
11
11
|
import { ProvingRequestType } from '@aztec/stdlib/proofs';
|
|
12
12
|
|
|
13
13
|
import { z } from 'zod';
|
|
@@ -31,6 +31,8 @@ export const ProverBrokerConfig = z.object({
|
|
|
31
31
|
proverBrokerBatchIntervalMs: z.number().int().nonnegative(),
|
|
32
32
|
/** The maximum number of epochs to keep results for */
|
|
33
33
|
proverBrokerMaxEpochsToKeepResultsFor: z.number().int().nonnegative(),
|
|
34
|
+
/** Enable debug replay mode for replaying proving jobs from stored inputs */
|
|
35
|
+
proverBrokerDebugReplayEnabled: z.boolean(),
|
|
34
36
|
});
|
|
35
37
|
|
|
36
38
|
export type ProverBrokerConfig = z.infer<typeof ProverBrokerConfig> &
|
|
@@ -74,6 +76,11 @@ export const proverBrokerConfigMappings: ConfigMappingsType<ProverBrokerConfig>
|
|
|
74
76
|
parseEnv: (val: string | undefined) => (val ? +val : undefined),
|
|
75
77
|
description: "The size of the prover broker's database. Will override the dataStoreMapSizeKb if set.",
|
|
76
78
|
},
|
|
79
|
+
proverBrokerDebugReplayEnabled: {
|
|
80
|
+
env: 'PROVER_BROKER_DEBUG_REPLAY_ENABLED',
|
|
81
|
+
description: 'Enable debug replay mode for replaying proving jobs from stored inputs',
|
|
82
|
+
...booleanConfigHelper(false),
|
|
83
|
+
},
|
|
77
84
|
...dataConfigMappings,
|
|
78
85
|
...l1ReaderConfigMappings,
|
|
79
86
|
...pickConfigMappings(chainConfigMappings, ['rollupVersion']),
|
|
@@ -102,6 +109,8 @@ export const ProverAgentConfig = z.object({
|
|
|
102
109
|
proverTestVerificationDelayMs: z.number().optional(),
|
|
103
110
|
/** Whether to abort pending proving jobs when the orchestrator is cancelled */
|
|
104
111
|
cancelJobsOnStop: z.boolean(),
|
|
112
|
+
/** Where to store proving results. Must be accessible to both prover node and agents. If not set will inline-encode the parameters */
|
|
113
|
+
proofStore: z.string().optional(),
|
|
105
114
|
});
|
|
106
115
|
|
|
107
116
|
export type ProverAgentConfig = z.infer<typeof ProverAgentConfig>;
|
|
@@ -162,4 +171,8 @@ export const proverAgentConfigMappings: ConfigMappingsType<ProverAgentConfig> =
|
|
|
162
171
|
'When false (default), jobs remain in the broker queue and can be reused on restart/reorg.',
|
|
163
172
|
...booleanConfigHelper(false),
|
|
164
173
|
},
|
|
174
|
+
proofStore: {
|
|
175
|
+
env: 'PROVER_PROOF_STORE',
|
|
176
|
+
description: 'Optional proof input store for the prover',
|
|
177
|
+
},
|
|
165
178
|
};
|
|
@@ -1,42 +1,20 @@
|
|
|
1
1
|
import { createLogger } from '@aztec/foundation/log';
|
|
2
|
+
import { createFileStore } from '@aztec/stdlib/file-store';
|
|
2
3
|
|
|
3
|
-
import {
|
|
4
|
+
import { FileStoreProofStore } from './file_store_proof_store.js';
|
|
4
5
|
import { InlineProofStore } from './inline_proof_store.js';
|
|
5
6
|
import type { ProofStore } from './proof_store.js';
|
|
6
7
|
|
|
7
|
-
export function createProofStore(
|
|
8
|
-
|
|
8
|
+
export async function createProofStore(
|
|
9
|
+
config: string | undefined,
|
|
10
|
+
logger = createLogger('prover-client:proof-store'),
|
|
11
|
+
): Promise<ProofStore> {
|
|
12
|
+
if (!config) {
|
|
9
13
|
logger.info('Creating inline proof store');
|
|
10
14
|
return new InlineProofStore();
|
|
11
|
-
} else if (config.startsWith('gs://')) {
|
|
12
|
-
try {
|
|
13
|
-
const url = new URL(config);
|
|
14
|
-
const bucket = url.host;
|
|
15
|
-
const path = url.pathname.replace(/^\/+/, '');
|
|
16
|
-
logger.info(`Creating google cloud proof store at ${bucket}`, { bucket, path });
|
|
17
|
-
return new GoogleCloudStorageProofStore(bucket, path);
|
|
18
|
-
} catch {
|
|
19
|
-
throw new Error(
|
|
20
|
-
`Invalid google cloud proof store definition: '${config}'. Supported values are 'gs://bucket-name/path/to/store'.`,
|
|
21
|
-
);
|
|
22
|
-
}
|
|
23
|
-
} else {
|
|
24
|
-
throw new Error(`Unknown proof store config: '${config}'. Supported values are 'gs://bucket-name/path/to/store'.`);
|
|
25
15
|
}
|
|
26
|
-
}
|
|
27
16
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
): Pick<ProofStore, 'getProofInput' | 'getProofOutput'> {
|
|
32
|
-
if (uri.startsWith('data://')) {
|
|
33
|
-
return createProofStore(undefined, logger);
|
|
34
|
-
} else if (uri.startsWith('gs://')) {
|
|
35
|
-
const url = new URL(uri);
|
|
36
|
-
const basePath = url.pathname.replace(/^\/+/, '').split('/').slice(0, -3);
|
|
37
|
-
url.pathname = basePath.join('/');
|
|
38
|
-
return createProofStore(uri, logger);
|
|
39
|
-
} else {
|
|
40
|
-
throw new Error(`Unknown proof store config: '${uri}'. Supported protocols are 'data://' and 'gs://'.`);
|
|
41
|
-
}
|
|
17
|
+
const fileStore = await createFileStore(config, logger);
|
|
18
|
+
logger.info(`Creating file store proof store at ${config}`);
|
|
19
|
+
return new FileStoreProofStore(fileStore);
|
|
42
20
|
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { jsonParseWithSchema, jsonStringify } from '@aztec/foundation/json-rpc';
|
|
2
|
+
import type { FileStore } from '@aztec/stdlib/file-store';
|
|
3
|
+
import {
|
|
4
|
+
type ProofUri,
|
|
5
|
+
type ProvingJobId,
|
|
6
|
+
type ProvingJobInputs,
|
|
7
|
+
type ProvingJobInputsMap,
|
|
8
|
+
ProvingJobResult,
|
|
9
|
+
type ProvingJobResultsMap,
|
|
10
|
+
getProvingJobInputClassFor,
|
|
11
|
+
} from '@aztec/stdlib/interfaces/server';
|
|
12
|
+
import { ProvingRequestType } from '@aztec/stdlib/proofs';
|
|
13
|
+
|
|
14
|
+
import type { ProofStore } from './proof_store.js';
|
|
15
|
+
|
|
16
|
+
const INPUTS_PATH = 'inputs';
|
|
17
|
+
const OUTPUTS_PATH = 'outputs';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* A proof store implementation backed by a generic FileStore.
|
|
21
|
+
* Supports any storage backend (GCS, S3, local filesystem) via the FileStore abstraction.
|
|
22
|
+
*/
|
|
23
|
+
export class FileStoreProofStore implements ProofStore {
|
|
24
|
+
constructor(private readonly fileStore: FileStore) {}
|
|
25
|
+
|
|
26
|
+
async saveProofInput<T extends ProvingRequestType>(
|
|
27
|
+
id: ProvingJobId,
|
|
28
|
+
type: T,
|
|
29
|
+
inputs: ProvingJobInputsMap[T],
|
|
30
|
+
): Promise<ProofUri> {
|
|
31
|
+
const path = `${INPUTS_PATH}/${ProvingRequestType[type]}/${id}`;
|
|
32
|
+
const uri = await this.fileStore.save(path, inputs.toBuffer());
|
|
33
|
+
return uri as ProofUri;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async saveProofOutput<T extends ProvingRequestType>(
|
|
37
|
+
id: ProvingJobId,
|
|
38
|
+
type: T,
|
|
39
|
+
result: ProvingJobResultsMap[T],
|
|
40
|
+
): Promise<ProofUri> {
|
|
41
|
+
const jobResult = { type, result } as ProvingJobResult;
|
|
42
|
+
const json = jsonStringify(jobResult);
|
|
43
|
+
const path = `${OUTPUTS_PATH}/${ProvingRequestType[type]}/${id}.json`;
|
|
44
|
+
const uri = await this.fileStore.save(path, Buffer.from(json, 'utf-8'));
|
|
45
|
+
return uri as ProofUri;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async getProofInput(uri: ProofUri): Promise<ProvingJobInputs> {
|
|
49
|
+
try {
|
|
50
|
+
const buffer = await this.fileStore.read(uri);
|
|
51
|
+
const type = this.extractTypeFromUri(uri);
|
|
52
|
+
const inputs = getProvingJobInputClassFor(type).fromBuffer(buffer);
|
|
53
|
+
return { inputs, type } as ProvingJobInputs;
|
|
54
|
+
} catch (err) {
|
|
55
|
+
throw new Error(`Error getting proof input at ${uri}: ${err}`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async getProofOutput(uri: ProofUri): Promise<ProvingJobResult> {
|
|
60
|
+
try {
|
|
61
|
+
const buffer = await this.fileStore.read(uri);
|
|
62
|
+
return jsonParseWithSchema(buffer.toString('utf-8'), ProvingJobResult);
|
|
63
|
+
} catch (err) {
|
|
64
|
+
throw new Error(`Error getting proof output at ${uri}: ${err}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
private extractTypeFromUri(uri: string): ProvingRequestType {
|
|
69
|
+
const url = new URL(uri);
|
|
70
|
+
const pathParts = url.pathname.split('/').filter(Boolean);
|
|
71
|
+
const typeString = pathParts.at(-2);
|
|
72
|
+
const type = typeString ? ProvingRequestType[typeString as keyof typeof ProvingRequestType] : undefined;
|
|
73
|
+
if (type === undefined) {
|
|
74
|
+
throw new Error(`Unrecognized proof type ${typeString} in URI ${uri}`);
|
|
75
|
+
}
|
|
76
|
+
return type;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AbortError } from '@aztec/foundation/error';
|
|
2
|
-
import { createLogger } from '@aztec/foundation/log';
|
|
2
|
+
import { type Logger, type LoggerBindings, createLogger } from '@aztec/foundation/log';
|
|
3
3
|
import { RunningPromise } from '@aztec/foundation/running-promise';
|
|
4
4
|
import { truncate } from '@aztec/foundation/string';
|
|
5
5
|
import { ProvingError } from '@aztec/stdlib/errors';
|
|
@@ -23,6 +23,7 @@ import { ProvingJobController, ProvingJobControllerStatus } from './proving_job_
|
|
|
23
23
|
export class ProvingAgent {
|
|
24
24
|
private currentJobController?: ProvingJobController;
|
|
25
25
|
private runningPromise: RunningPromise;
|
|
26
|
+
private log: Logger;
|
|
26
27
|
|
|
27
28
|
constructor(
|
|
28
29
|
/** The source of proving jobs */
|
|
@@ -35,8 +36,9 @@ export class ProvingAgent {
|
|
|
35
36
|
private proofAllowList: Array<ProvingRequestType> = [],
|
|
36
37
|
/** How long to wait between jobs */
|
|
37
38
|
private pollIntervalMs = 1000,
|
|
38
|
-
|
|
39
|
+
bindings?: LoggerBindings,
|
|
39
40
|
) {
|
|
41
|
+
this.log = createLogger('prover-client:proving-agent', bindings);
|
|
40
42
|
this.runningPromise = new RunningPromise(this.work.bind(this), this.log, this.pollIntervalMs);
|
|
41
43
|
}
|
|
42
44
|
|
|
@@ -159,6 +161,7 @@ export class ProvingAgent {
|
|
|
159
161
|
// no need to await this here. The controller will stay alive (in DONE state) until the result is send to the broker
|
|
160
162
|
void this.runningPromise.trigger();
|
|
161
163
|
},
|
|
164
|
+
this.log.getBindings(),
|
|
162
165
|
);
|
|
163
166
|
|
|
164
167
|
if (abortedProofJobId) {
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
type GetProvingJobResponse,
|
|
8
8
|
type ProofUri,
|
|
9
9
|
type ProvingJob,
|
|
10
|
+
type ProvingJobBrokerDebug,
|
|
10
11
|
type ProvingJobConsumer,
|
|
11
12
|
type ProvingJobFilter,
|
|
12
13
|
type ProvingJobId,
|
|
@@ -36,7 +37,7 @@ type EnqueuedProvingJob = Pick<ProvingJob, 'id' | 'epochNumber'>;
|
|
|
36
37
|
* A broker that manages proof requests and distributes them to workers based on their priority.
|
|
37
38
|
* It takes a backend that is responsible for storing and retrieving proof requests and results.
|
|
38
39
|
*/
|
|
39
|
-
export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer, Traceable {
|
|
40
|
+
export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer, ProvingJobBrokerDebug, Traceable {
|
|
40
41
|
private queues: ProvingQueues = {
|
|
41
42
|
[ProvingRequestType.PUBLIC_VM]: new PriorityMemoryQueue<EnqueuedProvingJob>(provingJobComparator),
|
|
42
43
|
[ProvingRequestType.PUBLIC_CHONK_VERIFIER]: new PriorityMemoryQueue<EnqueuedProvingJob>(provingJobComparator),
|
|
@@ -114,6 +115,8 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer, Tr
|
|
|
114
115
|
|
|
115
116
|
private started = false;
|
|
116
117
|
|
|
118
|
+
private debugReplayEnabled: boolean;
|
|
119
|
+
|
|
117
120
|
public constructor(
|
|
118
121
|
private database: ProvingBrokerDatabase,
|
|
119
122
|
{
|
|
@@ -121,6 +124,7 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer, Tr
|
|
|
121
124
|
proverBrokerPollIntervalMs,
|
|
122
125
|
proverBrokerJobMaxRetries,
|
|
123
126
|
proverBrokerMaxEpochsToKeepResultsFor,
|
|
127
|
+
proverBrokerDebugReplayEnabled,
|
|
124
128
|
}: Required<
|
|
125
129
|
Pick<
|
|
126
130
|
ProverBrokerConfig,
|
|
@@ -128,6 +132,7 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer, Tr
|
|
|
128
132
|
| 'proverBrokerPollIntervalMs'
|
|
129
133
|
| 'proverBrokerJobMaxRetries'
|
|
130
134
|
| 'proverBrokerMaxEpochsToKeepResultsFor'
|
|
135
|
+
| 'proverBrokerDebugReplayEnabled'
|
|
131
136
|
>
|
|
132
137
|
> = defaultProverBrokerConfig,
|
|
133
138
|
client: TelemetryClient = getTelemetryClient(),
|
|
@@ -139,6 +144,7 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer, Tr
|
|
|
139
144
|
this.jobTimeoutMs = proverBrokerJobTimeoutMs!;
|
|
140
145
|
this.maxRetries = proverBrokerJobMaxRetries!;
|
|
141
146
|
this.maxEpochsToKeepResultsFor = proverBrokerMaxEpochsToKeepResultsFor!;
|
|
147
|
+
this.debugReplayEnabled = proverBrokerDebugReplayEnabled ?? false;
|
|
142
148
|
}
|
|
143
149
|
|
|
144
150
|
private measureQueueDepth: MonitorCallback = (type: ProvingRequestType) => {
|
|
@@ -241,6 +247,29 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer, Tr
|
|
|
241
247
|
return Promise.resolve(this.#reportProvingJobProgress(id, startedAt, filter));
|
|
242
248
|
}
|
|
243
249
|
|
|
250
|
+
public async replayProvingJob(
|
|
251
|
+
jobId: ProvingJobId,
|
|
252
|
+
type: ProvingRequestType,
|
|
253
|
+
epochNumber: EpochNumber,
|
|
254
|
+
inputsUri: ProofUri,
|
|
255
|
+
): Promise<ProvingJobStatus> {
|
|
256
|
+
if (!this.debugReplayEnabled) {
|
|
257
|
+
throw new Error('Debug replay not enabled. Set PROVER_BROKER_DEBUG_REPLAY_ENABLED=true');
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
this.logger.info(`Replaying proving job`, { provingJobId: jobId, epochNumber, inputsUri });
|
|
261
|
+
|
|
262
|
+
// Clear existing state and enqueue
|
|
263
|
+
this.cleanUpProvingJobState([jobId]);
|
|
264
|
+
|
|
265
|
+
const job: ProvingJob = { id: jobId, type, epochNumber, inputsUri };
|
|
266
|
+
this.jobsCache.set(jobId, job);
|
|
267
|
+
await this.database.addProvingJob(job);
|
|
268
|
+
this.enqueueJobInternal(job);
|
|
269
|
+
|
|
270
|
+
return { status: 'in-queue' };
|
|
271
|
+
}
|
|
272
|
+
|
|
244
273
|
async #enqueueProvingJob(job: ProvingJob): Promise<ProvingJobStatus> {
|
|
245
274
|
// We return the job status at the start of this call
|
|
246
275
|
const jobStatus = this.#getProvingJobStatus(job.id);
|
|
@@ -285,7 +314,7 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer, Tr
|
|
|
285
314
|
// notify listeners of the cancellation
|
|
286
315
|
if (!this.resultsCache.has(id)) {
|
|
287
316
|
this.logger.info(`Cancelling job id=${id}`, { provingJobId: id });
|
|
288
|
-
await this.#reportProvingJobError(id, 'Aborted', false);
|
|
317
|
+
await this.#reportProvingJobError(id, 'Aborted', false, undefined, true);
|
|
289
318
|
}
|
|
290
319
|
}
|
|
291
320
|
|
|
@@ -366,6 +395,7 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer, Tr
|
|
|
366
395
|
err: string,
|
|
367
396
|
retry = false,
|
|
368
397
|
filter?: ProvingJobFilter,
|
|
398
|
+
aborted = false,
|
|
369
399
|
): Promise<GetProvingJobResponse | undefined> {
|
|
370
400
|
const info = this.inProgress.get(id);
|
|
371
401
|
const item = this.jobsCache.get(id);
|
|
@@ -426,7 +456,11 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer, Tr
|
|
|
426
456
|
this.promises.get(id)!.resolve(result);
|
|
427
457
|
this.completedJobNotifications.push(id);
|
|
428
458
|
|
|
429
|
-
|
|
459
|
+
if (aborted) {
|
|
460
|
+
this.instrumentation.incAbortedJobs(item.type);
|
|
461
|
+
} else {
|
|
462
|
+
this.instrumentation.incRejectedJobs(item.type);
|
|
463
|
+
}
|
|
430
464
|
if (info) {
|
|
431
465
|
const duration = this.msTimeSource() - info.startedAt;
|
|
432
466
|
this.instrumentation.recordJobDuration(item.type, duration);
|
|
@@ -598,10 +632,26 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer, Tr
|
|
|
598
632
|
const now = this.msTimeSource();
|
|
599
633
|
const msSinceLastUpdate = now - metadata.lastUpdatedAt;
|
|
600
634
|
if (msSinceLastUpdate >= this.jobTimeoutMs) {
|
|
601
|
-
this.logger.warn(`Proving job id=${id} timed out. Adding it back to the queue.`, { provingJobId: id });
|
|
602
635
|
this.inProgress.delete(id);
|
|
603
|
-
this.enqueueJobInternal(item);
|
|
604
636
|
this.instrumentation.incTimedOutJobs(item.type);
|
|
637
|
+
|
|
638
|
+
const retries = this.retries.get(id) ?? 0;
|
|
639
|
+
if (retries + 1 < this.maxRetries && !this.isJobStale(item)) {
|
|
640
|
+
this.logger.warn(`Proving job id=${id} timed out. Re-enqueueing (retry ${retries + 1}/${this.maxRetries}).`, {
|
|
641
|
+
provingJobId: id,
|
|
642
|
+
});
|
|
643
|
+
this.retries.set(id, retries + 1);
|
|
644
|
+
this.enqueueJobInternal(item);
|
|
645
|
+
} else {
|
|
646
|
+
this.logger.error(`Proving job id=${id} timed out after ${retries + 1} attempts. Marking as failed.`, {
|
|
647
|
+
provingJobId: id,
|
|
648
|
+
});
|
|
649
|
+
const result: ProvingJobSettledResult = { status: 'rejected', reason: 'Timed out' };
|
|
650
|
+
this.resultsCache.set(id, result);
|
|
651
|
+
this.promises.get(id)?.resolve(result);
|
|
652
|
+
this.completedJobNotifications.push(id);
|
|
653
|
+
this.instrumentation.incRejectedJobs(item.type);
|
|
654
|
+
}
|
|
605
655
|
}
|
|
606
656
|
}
|
|
607
657
|
}
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
type ObservableResult,
|
|
9
9
|
type TelemetryClient,
|
|
10
10
|
type UpDownCounter,
|
|
11
|
+
createUpDownCounterWithDefault,
|
|
11
12
|
} from '@aztec/telemetry-client';
|
|
12
13
|
|
|
13
14
|
export type MonitorCallback = (proofType: ProvingRequestType) => number;
|
|
@@ -17,6 +18,7 @@ export class ProvingBrokerInstrumentation {
|
|
|
17
18
|
private activeJobs: ObservableGauge;
|
|
18
19
|
private resolvedJobs: UpDownCounter;
|
|
19
20
|
private rejectedJobs: UpDownCounter;
|
|
21
|
+
private abortedJobs: UpDownCounter;
|
|
20
22
|
private timedOutJobs: UpDownCounter;
|
|
21
23
|
private cachedJobs: UpDownCounter;
|
|
22
24
|
private totalJobs: UpDownCounter;
|
|
@@ -31,17 +33,22 @@ export class ProvingBrokerInstrumentation {
|
|
|
31
33
|
|
|
32
34
|
this.activeJobs = meter.createObservableGauge(Metrics.PROVING_QUEUE_ACTIVE_JOBS);
|
|
33
35
|
|
|
34
|
-
|
|
36
|
+
const provingJobTypes = Object.values(ProvingRequestType).filter(v => typeof v === 'string');
|
|
37
|
+
const provingJobAttrs = { [Attributes.PROVING_JOB_TYPE]: provingJobTypes };
|
|
35
38
|
|
|
36
|
-
this.
|
|
39
|
+
this.resolvedJobs = createUpDownCounterWithDefault(meter, Metrics.PROVING_QUEUE_RESOLVED_JOBS, provingJobAttrs);
|
|
37
40
|
|
|
38
|
-
this.
|
|
41
|
+
this.rejectedJobs = createUpDownCounterWithDefault(meter, Metrics.PROVING_QUEUE_REJECTED_JOBS, provingJobAttrs);
|
|
39
42
|
|
|
40
|
-
this.
|
|
43
|
+
this.abortedJobs = createUpDownCounterWithDefault(meter, Metrics.PROVING_QUEUE_ABORTED_JOBS, provingJobAttrs);
|
|
41
44
|
|
|
42
|
-
this.
|
|
45
|
+
this.retriedJobs = createUpDownCounterWithDefault(meter, Metrics.PROVING_QUEUE_RETRIED_JOBS, provingJobAttrs);
|
|
43
46
|
|
|
44
|
-
this.
|
|
47
|
+
this.timedOutJobs = createUpDownCounterWithDefault(meter, Metrics.PROVING_QUEUE_TIMED_OUT_JOBS, provingJobAttrs);
|
|
48
|
+
|
|
49
|
+
this.cachedJobs = createUpDownCounterWithDefault(meter, Metrics.PROVING_QUEUE_CACHED_JOBS, provingJobAttrs);
|
|
50
|
+
|
|
51
|
+
this.totalJobs = createUpDownCounterWithDefault(meter, Metrics.PROVING_QUEUE_TOTAL_JOBS, provingJobAttrs);
|
|
45
52
|
|
|
46
53
|
this.jobWait = meter.createHistogram(Metrics.PROVING_QUEUE_JOB_WAIT);
|
|
47
54
|
|
|
@@ -68,6 +75,12 @@ export class ProvingBrokerInstrumentation {
|
|
|
68
75
|
});
|
|
69
76
|
}
|
|
70
77
|
|
|
78
|
+
incAbortedJobs(proofType: ProvingRequestType) {
|
|
79
|
+
this.abortedJobs.add(1, {
|
|
80
|
+
[Attributes.PROVING_JOB_TYPE]: ProvingRequestType[proofType],
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
71
84
|
incRetriedJobs(proofType: ProvingRequestType) {
|
|
72
85
|
this.retriedJobs.add(1, {
|
|
73
86
|
[Attributes.PROVING_JOB_TYPE]: ProvingRequestType[proofType],
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { EpochNumber } from '@aztec/foundation/branded-types';
|
|
2
2
|
import { randomBytes } from '@aztec/foundation/crypto/random';
|
|
3
3
|
import { AbortError } from '@aztec/foundation/error';
|
|
4
|
-
import { createLogger } from '@aztec/foundation/log';
|
|
4
|
+
import { type Logger, type LoggerBindings, createLogger } from '@aztec/foundation/log';
|
|
5
5
|
import type {
|
|
6
6
|
ProvingJobId,
|
|
7
7
|
ProvingJobInputs,
|
|
@@ -21,6 +21,7 @@ export class ProvingJobController {
|
|
|
21
21
|
private promise?: Promise<void>;
|
|
22
22
|
private abortController = new AbortController();
|
|
23
23
|
private result?: ProvingJobResultsMap[ProvingRequestType] | Error;
|
|
24
|
+
private log: Logger;
|
|
24
25
|
|
|
25
26
|
constructor(
|
|
26
27
|
private jobId: ProvingJobId,
|
|
@@ -29,8 +30,13 @@ export class ProvingJobController {
|
|
|
29
30
|
private startedAt: number,
|
|
30
31
|
private circuitProver: ServerCircuitProver,
|
|
31
32
|
private onComplete: () => void,
|
|
32
|
-
|
|
33
|
-
) {
|
|
33
|
+
bindings?: LoggerBindings,
|
|
34
|
+
) {
|
|
35
|
+
this.log = createLogger('prover-client:proving-agent:job-controller', {
|
|
36
|
+
instanceId: randomBytes(4).toString('hex'),
|
|
37
|
+
...bindings,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
34
40
|
|
|
35
41
|
public start(): void {
|
|
36
42
|
if (this.status !== ProvingJobControllerStatus.IDLE) {
|