@aztec-labs/prover-client 6.0.0-nightly.20260829
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 +1 -0
- package/dest/config.d.ts +17 -0
- package/dest/config.d.ts.map +1 -0
- package/dest/config.js +63 -0
- package/dest/index.d.ts +4 -0
- package/dest/index.d.ts.map +1 -0
- package/dest/index.js +2 -0
- package/dest/light/index.d.ts +2 -0
- package/dest/light/index.d.ts.map +1 -0
- package/dest/light/index.js +1 -0
- package/dest/light/lightweight_checkpoint_builder.d.ts +58 -0
- package/dest/light/lightweight_checkpoint_builder.d.ts.map +1 -0
- package/dest/light/lightweight_checkpoint_builder.js +243 -0
- package/dest/mocks/fixtures.d.ts +20 -0
- package/dest/mocks/fixtures.d.ts.map +1 -0
- package/dest/mocks/fixtures.js +96 -0
- package/dest/mocks/test_context.d.ts +93 -0
- package/dest/mocks/test_context.d.ts.map +1 -0
- package/dest/mocks/test_context.js +354 -0
- package/dest/orchestrator/block-building-helpers.d.ts +67 -0
- package/dest/orchestrator/block-building-helpers.d.ts.map +1 -0
- package/dest/orchestrator/block-building-helpers.js +274 -0
- package/dest/orchestrator/block-proving-state.d.ts +107 -0
- package/dest/orchestrator/block-proving-state.d.ts.map +1 -0
- package/dest/orchestrator/block-proving-state.js +339 -0
- package/dest/orchestrator/checkpoint-proving-state.d.ts +67 -0
- package/dest/orchestrator/checkpoint-proving-state.d.ts.map +1 -0
- package/dest/orchestrator/checkpoint-proving-state.js +175 -0
- package/dest/orchestrator/checkpoint-sub-tree-orchestrator.d.ts +169 -0
- package/dest/orchestrator/checkpoint-sub-tree-orchestrator.d.ts.map +1 -0
- package/dest/orchestrator/checkpoint-sub-tree-orchestrator.js +1047 -0
- 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 +4 -0
- package/dest/orchestrator/index.d.ts.map +1 -0
- package/dest/orchestrator/index.js +3 -0
- package/dest/orchestrator/orchestrator_metrics.d.ts +8 -0
- package/dest/orchestrator/orchestrator_metrics.d.ts.map +1 -0
- package/dest/orchestrator/orchestrator_metrics.js +13 -0
- package/dest/orchestrator/proving-scheduler.d.ts +81 -0
- package/dest/orchestrator/proving-scheduler.d.ts.map +1 -0
- package/dest/orchestrator/proving-scheduler.js +120 -0
- package/dest/orchestrator/top-tree-orchestrator.d.ts +89 -0
- package/dest/orchestrator/top-tree-orchestrator.d.ts.map +1 -0
- package/dest/orchestrator/top-tree-orchestrator.js +233 -0
- package/dest/orchestrator/top-tree-proving-state.d.ts +61 -0
- package/dest/orchestrator/top-tree-proving-state.d.ts.map +1 -0
- package/dest/orchestrator/top-tree-proving-state.js +185 -0
- package/dest/orchestrator/tx-proving-state.d.ts +37 -0
- package/dest/orchestrator/tx-proving-state.d.ts.map +1 -0
- package/dest/orchestrator/tx-proving-state.js +77 -0
- package/dest/prover-client/factory.d.ts +6 -0
- package/dest/prover-client/factory.d.ts.map +1 -0
- package/dest/prover-client/factory.js +5 -0
- package/dest/prover-client/index.d.ts +3 -0
- package/dest/prover-client/index.d.ts.map +1 -0
- package/dest/prover-client/index.js +2 -0
- package/dest/prover-client/prover-client.d.ts +99 -0
- package/dest/prover-client/prover-client.d.ts.map +1 -0
- package/dest/prover-client/prover-client.js +163 -0
- package/dest/proving_broker/broker_prover_facade.d.ts +52 -0
- package/dest/proving_broker/broker_prover_facade.d.ts.map +1 -0
- package/dest/proving_broker/broker_prover_facade.js +360 -0
- package/dest/proving_broker/config.d.ts +40 -0
- package/dest/proving_broker/config.d.ts.map +1 -0
- package/dest/proving_broker/config.js +138 -0
- package/dest/proving_broker/factory.d.ts +5 -0
- package/dest/proving_broker/factory.d.ts.map +1 -0
- package/dest/proving_broker/factory.js +13 -0
- package/dest/proving_broker/fixtures.d.ts +6 -0
- package/dest/proving_broker/fixtures.d.ts.map +1 -0
- package/dest/proving_broker/fixtures.js +13 -0
- package/dest/proving_broker/index.d.ts +11 -0
- package/dest/proving_broker/index.d.ts.map +1 -0
- package/dest/proving_broker/index.js +10 -0
- package/dest/proving_broker/proof_store/factory.d.ts +3 -0
- package/dest/proving_broker/proof_store/factory.d.ts.map +1 -0
- package/dest/proving_broker/proof_store/factory.js +13 -0
- 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 +5 -0
- package/dest/proving_broker/proof_store/index.d.ts.map +1 -0
- package/dest/proving_broker/proof_store/index.js +4 -0
- package/dest/proving_broker/proof_store/inline_proof_store.d.ts +15 -0
- package/dest/proving_broker/proof_store/inline_proof_store.d.ts.map +1 -0
- package/dest/proving_broker/proof_store/inline_proof_store.js +41 -0
- package/dest/proving_broker/proof_store/proof_store.d.ts +36 -0
- package/dest/proving_broker/proof_store/proof_store.d.ts.map +1 -0
- package/dest/proving_broker/proof_store/proof_store.js +3 -0
- package/dest/proving_broker/proving_agent.d.ts +42 -0
- package/dest/proving_broker/proving_agent.d.ts.map +1 -0
- package/dest/proving_broker/proving_agent.js +155 -0
- package/dest/proving_broker/proving_broker.d.ts +77 -0
- package/dest/proving_broker/proving_broker.d.ts.map +1 -0
- package/dest/proving_broker/proving_broker.js +780 -0
- package/dest/proving_broker/proving_broker_database/memory.d.ts +20 -0
- package/dest/proving_broker/proving_broker_database/memory.d.ts.map +1 -0
- package/dest/proving_broker/proving_broker_database/memory.js +67 -0
- package/dest/proving_broker/proving_broker_database/persisted.d.ts +40 -0
- package/dest/proving_broker/proving_broker_database/persisted.d.ts.map +1 -0
- package/dest/proving_broker/proving_broker_database/persisted.js +623 -0
- package/dest/proving_broker/proving_broker_database.d.ts +66 -0
- package/dest/proving_broker/proving_broker_database.d.ts.map +1 -0
- package/dest/proving_broker/proving_broker_database.js +3 -0
- package/dest/proving_broker/proving_broker_instrumentation.d.ts +31 -0
- package/dest/proving_broker/proving_broker_instrumentation.d.ts.map +1 -0
- package/dest/proving_broker/proving_broker_instrumentation.js +97 -0
- package/dest/proving_broker/proving_job_controller.d.ts +34 -0
- package/dest/proving_broker/proving_job_controller.d.ts.map +1 -0
- package/dest/proving_broker/proving_job_controller.js +184 -0
- package/dest/proving_broker/rpc.d.ts +38 -0
- package/dest/proving_broker/rpc.d.ts.map +1 -0
- package/dest/proving_broker/rpc.js +138 -0
- 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_proof_store.d.ts +9 -0
- package/dest/test/mock_proof_store.d.ts.map +1 -0
- package/dest/test/mock_proof_store.js +10 -0
- package/dest/test/mock_prover.d.ts +38 -0
- package/dest/test/mock_prover.d.ts.map +1 -0
- package/dest/test/mock_prover.js +91 -0
- package/package.json +113 -0
- package/src/config.ts +87 -0
- package/src/index.ts +4 -0
- package/src/light/index.ts +1 -0
- package/src/light/lightweight_checkpoint_builder.ts +342 -0
- package/src/mocks/fixtures.ts +120 -0
- package/src/mocks/test_context.ts +477 -0
- package/src/orchestrator/block-building-helpers.ts +486 -0
- package/src/orchestrator/block-proving-state.ts +457 -0
- package/src/orchestrator/checkpoint-proving-state.ts +247 -0
- package/src/orchestrator/checkpoint-sub-tree-orchestrator.ts +1010 -0
- package/src/orchestrator/chonk-cache.ts +99 -0
- package/src/orchestrator/index.ts +12 -0
- package/src/orchestrator/orchestrator_metrics.ts +18 -0
- package/src/orchestrator/proving-scheduler.ts +160 -0
- package/src/orchestrator/top-tree-orchestrator.ts +394 -0
- package/src/orchestrator/top-tree-proving-state.ts +219 -0
- package/src/orchestrator/tx-proving-state.ts +121 -0
- package/src/prover-client/factory.ts +18 -0
- package/src/prover-client/index.ts +2 -0
- package/src/prover-client/prover-client.ts +286 -0
- package/src/proving_broker/broker_prover_facade.ts +626 -0
- package/src/proving_broker/config.ts +178 -0
- package/src/proving_broker/factory.ts +19 -0
- package/src/proving_broker/fixtures.ts +20 -0
- package/src/proving_broker/index.ts +10 -0
- package/src/proving_broker/proof_store/factory.ts +20 -0
- package/src/proving_broker/proof_store/file_store_proof_store.ts +78 -0
- package/src/proving_broker/proof_store/index.ts +4 -0
- package/src/proving_broker/proof_store/inline_proof_store.ts +63 -0
- package/src/proving_broker/proof_store/proof_store.ts +54 -0
- package/src/proving_broker/proving_agent.ts +210 -0
- package/src/proving_broker/proving_broker.ts +888 -0
- package/src/proving_broker/proving_broker_database/memory.ts +78 -0
- package/src/proving_broker/proving_broker_database/persisted.ts +296 -0
- package/src/proving_broker/proving_broker_database.ts +75 -0
- package/src/proving_broker/proving_broker_instrumentation.ts +133 -0
- package/src/proving_broker/proving_job_controller.ts +199 -0
- package/src/proving_broker/rpc.ts +131 -0
- package/src/test/epoch_settlement.ts +82 -0
- package/src/test/index.ts +1 -0
- package/src/test/mock_proof_store.ts +14 -0
- package/src/test/mock_prover.ts +304 -0
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { EpochNumber } from '@aztec-labs/foundation/branded-types';
|
|
2
|
+
import {
|
|
3
|
+
type ProofUri,
|
|
4
|
+
type ProvingJob,
|
|
5
|
+
type ProvingJobId,
|
|
6
|
+
type ProvingJobSettledResult,
|
|
7
|
+
getEpochFromProvingJobId,
|
|
8
|
+
} from '@aztec-labs/stdlib/interfaces/server';
|
|
9
|
+
|
|
10
|
+
import type { ProvingBrokerDatabase } from '../proving_broker_database.js';
|
|
11
|
+
|
|
12
|
+
export class InMemoryBrokerDatabase implements ProvingBrokerDatabase {
|
|
13
|
+
private jobs = new Map<ProvingJobId, ProvingJob>();
|
|
14
|
+
private results = new Map<ProvingJobId, ProvingJobSettledResult>();
|
|
15
|
+
|
|
16
|
+
getProvingJob(id: ProvingJobId): ProvingJob | undefined {
|
|
17
|
+
return this.jobs.get(id);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
getProvingJobInputs(id: ProvingJobId): Promise<ProofUri | undefined> {
|
|
21
|
+
return Promise.resolve(this.jobs.get(id)?.inputsUri);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
getProvingJobResult(id: ProvingJobId): Promise<ProvingJobSettledResult | undefined> {
|
|
25
|
+
return Promise.resolve(this.results.get(id));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
addProvingJob(job: ProvingJob): Promise<void> {
|
|
29
|
+
this.jobs.set(job.id, job);
|
|
30
|
+
return Promise.resolve();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
setProvingJobResult(id: ProvingJobId, value: ProofUri): Promise<void> {
|
|
34
|
+
this.results.set(id, { status: 'fulfilled', value });
|
|
35
|
+
return Promise.resolve();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
setProvingJobError(id: ProvingJobId, reason: string): Promise<void> {
|
|
39
|
+
this.results.set(id, { status: 'rejected', reason });
|
|
40
|
+
return Promise.resolve();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
setProvingJobAborted(id: ProvingJobId): Promise<void> {
|
|
44
|
+
this.results.set(id, { status: 'aborted' });
|
|
45
|
+
return Promise.resolve();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
deleteProvingJobResult(id: ProvingJobId): Promise<void> {
|
|
49
|
+
this.results.delete(id);
|
|
50
|
+
return Promise.resolve();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
deleteProvingJobs(ids: ProvingJobId[]): Promise<void> {
|
|
54
|
+
for (const id of ids) {
|
|
55
|
+
this.jobs.delete(id);
|
|
56
|
+
this.results.delete(id);
|
|
57
|
+
}
|
|
58
|
+
return Promise.resolve();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
deleteAllProvingJobsOlderThanEpoch(epochNumber: EpochNumber): Promise<void> {
|
|
62
|
+
const toDelete = [
|
|
63
|
+
...Array.from(this.jobs.keys()).filter(x => getEpochFromProvingJobId(x) < epochNumber),
|
|
64
|
+
...Array.from(this.results.keys()).filter(x => getEpochFromProvingJobId(x) < epochNumber),
|
|
65
|
+
];
|
|
66
|
+
return this.deleteProvingJobs(toDelete);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async *allProvingJobs(): AsyncIterableIterator<[ProvingJob, ProvingJobSettledResult | undefined]> {
|
|
70
|
+
for (const item of this.jobs.values()) {
|
|
71
|
+
yield [item, this.results.get(item.id)] as const;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
close(): Promise<void> {
|
|
76
|
+
return Promise.resolve();
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
import { EpochNumber } from '@aztec-labs/foundation/branded-types';
|
|
2
|
+
import { jsonParseWithSchema, jsonStringify } from '@aztec-labs/foundation/json-rpc';
|
|
3
|
+
import { type Logger, createLogger } from '@aztec-labs/foundation/log';
|
|
4
|
+
import { BatchQueue } from '@aztec-labs/foundation/queue';
|
|
5
|
+
import type { AztecAsyncKVStore, AztecAsyncMap } from '@aztec-labs/kv-store';
|
|
6
|
+
import { openVersionedStoreAt } from '@aztec-labs/kv-store/lmdb-v2';
|
|
7
|
+
import {
|
|
8
|
+
type ProofUri,
|
|
9
|
+
ProvingJob,
|
|
10
|
+
type ProvingJobId,
|
|
11
|
+
ProvingJobSettledResult,
|
|
12
|
+
getEpochFromProvingJobId,
|
|
13
|
+
} from '@aztec-labs/stdlib/interfaces/server';
|
|
14
|
+
import {
|
|
15
|
+
Attributes,
|
|
16
|
+
LmdbMetrics,
|
|
17
|
+
type TelemetryClient,
|
|
18
|
+
type Tracer,
|
|
19
|
+
getTelemetryClient,
|
|
20
|
+
trackSpan,
|
|
21
|
+
} from '@aztec-labs/telemetry-client';
|
|
22
|
+
import { mkdir, readdir } from 'fs/promises';
|
|
23
|
+
import { join } from 'path';
|
|
24
|
+
|
|
25
|
+
import type { ProverBrokerConfig } from '../config.js';
|
|
26
|
+
import type { ProvingBrokerDatabase } from '../proving_broker_database.js';
|
|
27
|
+
|
|
28
|
+
class SingleEpochDatabase {
|
|
29
|
+
public static readonly SCHEMA_VERSION = 2;
|
|
30
|
+
|
|
31
|
+
private jobs: AztecAsyncMap<ProvingJobId, string>;
|
|
32
|
+
private jobResults: AztecAsyncMap<ProvingJobId, string>;
|
|
33
|
+
|
|
34
|
+
constructor(public readonly store: AztecAsyncKVStore) {
|
|
35
|
+
this.jobs = store.openMap('proving_jobs');
|
|
36
|
+
this.jobResults = store.openMap('proving_job_results');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
estimateSize() {
|
|
40
|
+
return this.store.estimateSize();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async batchWrite(
|
|
44
|
+
jobs: ProvingJob[],
|
|
45
|
+
results: Array<[ProvingJobId, ProvingJobSettledResult]>,
|
|
46
|
+
deletedResults: ProvingJobId[] = [],
|
|
47
|
+
) {
|
|
48
|
+
await this.store.transactionAsync(async () => {
|
|
49
|
+
for (const job of jobs) {
|
|
50
|
+
await this.jobs.set(job.id, jsonStringify(job));
|
|
51
|
+
}
|
|
52
|
+
for (const [id, result] of results) {
|
|
53
|
+
await this.jobResults.set(id, jsonStringify(result));
|
|
54
|
+
}
|
|
55
|
+
// Deletes are applied after sets so that, if a set and a delete for the same id land in one
|
|
56
|
+
// batch (e.g. an abort immediately followed by a revive's result-delete), the delete wins.
|
|
57
|
+
for (const id of deletedResults) {
|
|
58
|
+
await this.jobResults.delete(id);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async *allProvingJobs(): AsyncIterableIterator<[ProvingJob, ProvingJobSettledResult | undefined]> {
|
|
64
|
+
for await (const jobStr of this.jobs.valuesAsync()) {
|
|
65
|
+
const job = jsonParseWithSchema(jobStr, ProvingJob);
|
|
66
|
+
const resultStr = await this.jobResults.getAsync(job.id);
|
|
67
|
+
const result = resultStr ? jsonParseWithSchema(resultStr, ProvingJobSettledResult) : undefined;
|
|
68
|
+
yield [job, result];
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async getProvingJobInputs(id: ProvingJobId): Promise<ProofUri | undefined> {
|
|
73
|
+
const jobStr = await this.jobs.getAsync(id);
|
|
74
|
+
return jobStr ? jsonParseWithSchema(jobStr, ProvingJob).inputsUri : undefined;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async getProvingJobResult(id: ProvingJobId): Promise<ProvingJobSettledResult | undefined> {
|
|
78
|
+
const resultStr = await this.jobResults.getAsync(id);
|
|
79
|
+
return resultStr ? jsonParseWithSchema(resultStr, ProvingJobSettledResult) : undefined;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async setProvingJobError(id: ProvingJobId, reason: string): Promise<void> {
|
|
83
|
+
const result: ProvingJobSettledResult = { status: 'rejected', reason };
|
|
84
|
+
await this.jobResults.set(id, jsonStringify(result));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async setProvingJobAborted(id: ProvingJobId): Promise<void> {
|
|
88
|
+
const result: ProvingJobSettledResult = { status: 'aborted' };
|
|
89
|
+
await this.jobResults.set(id, jsonStringify(result));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async setProvingJobResult(id: ProvingJobId, value: ProofUri): Promise<void> {
|
|
93
|
+
const result: ProvingJobSettledResult = { status: 'fulfilled', value };
|
|
94
|
+
await this.jobResults.set(id, jsonStringify(result));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
delete() {
|
|
98
|
+
return this.store.delete();
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
close() {
|
|
102
|
+
return this.store.close();
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* An item queued for a batched write. A {@link ProvingJob} adds/updates a job; a `[id, result]`
|
|
108
|
+
* tuple sets a job's settled result; a `[id, undefined]` tuple deletes a job's result. Deletes ride
|
|
109
|
+
* the same queue as writes so they stay FIFO-ordered against them: a revive's result-delete must
|
|
110
|
+
* land after the abort that preceded it, otherwise a still-queued abort could flush later and
|
|
111
|
+
* resurrect the cancelled state on disk.
|
|
112
|
+
*/
|
|
113
|
+
type BrokerWrite = ProvingJob | [ProvingJobId, ProvingJobSettledResult | undefined];
|
|
114
|
+
|
|
115
|
+
export class KVBrokerDatabase implements ProvingBrokerDatabase {
|
|
116
|
+
private metrics: LmdbMetrics;
|
|
117
|
+
|
|
118
|
+
private batchQueue: BatchQueue<BrokerWrite, number>;
|
|
119
|
+
|
|
120
|
+
public readonly tracer: Tracer;
|
|
121
|
+
|
|
122
|
+
private constructor(
|
|
123
|
+
private epochs: Map<number, SingleEpochDatabase>,
|
|
124
|
+
private config: ProverBrokerConfig,
|
|
125
|
+
client: TelemetryClient = getTelemetryClient(),
|
|
126
|
+
private logger: Logger,
|
|
127
|
+
) {
|
|
128
|
+
this.metrics = new LmdbMetrics(
|
|
129
|
+
client.getMeter('KVBrokerDatabase'),
|
|
130
|
+
{
|
|
131
|
+
[Attributes.DB_DATA_TYPE]: 'prover-broker',
|
|
132
|
+
},
|
|
133
|
+
() => this.estimateSize(),
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
this.tracer = client.getTracer('KVBrokerDatabase');
|
|
137
|
+
|
|
138
|
+
this.batchQueue = new BatchQueue(
|
|
139
|
+
(items, key) => this.commitWrites(items, key),
|
|
140
|
+
config.proverBrokerBatchSize,
|
|
141
|
+
config.proverBrokerBatchIntervalMs,
|
|
142
|
+
createLogger('proving-client:proving-broker-database:batch-queue'),
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// exposed for testing
|
|
147
|
+
public async commitWrites(items: Array<BrokerWrite>, epochNumber: number) {
|
|
148
|
+
const jobsToAdd = items.filter((item): item is ProvingJob => 'id' in item);
|
|
149
|
+
const resultOps = items.filter((item): item is [ProvingJobId, ProvingJobSettledResult | undefined] =>
|
|
150
|
+
Array.isArray(item),
|
|
151
|
+
);
|
|
152
|
+
const resultsToSet = resultOps.filter((op): op is [ProvingJobId, ProvingJobSettledResult] => op[1] !== undefined);
|
|
153
|
+
const resultsToDelete = resultOps.filter(op => op[1] === undefined).map(([id]) => id);
|
|
154
|
+
|
|
155
|
+
const db = await this.getEpochDatabase(EpochNumber(epochNumber));
|
|
156
|
+
await db.batchWrite(jobsToAdd, resultsToSet, resultsToDelete);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
private async estimateSize() {
|
|
160
|
+
const sizes = await Promise.all(Array.from(this.epochs.values()).map(x => x.estimateSize()));
|
|
161
|
+
return {
|
|
162
|
+
mappingSize: this.config.dataStoreMapSizeKb,
|
|
163
|
+
physicalFileSize: sizes.reduce((prev, curr) => prev + curr.physicalFileSize, 0),
|
|
164
|
+
numItems: sizes.reduce((prev, curr) => prev + curr.numItems, 0),
|
|
165
|
+
actualSize: sizes.reduce((prev, curr) => prev + curr.actualSize, 0),
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
public static async new(
|
|
170
|
+
config: ProverBrokerConfig,
|
|
171
|
+
client: TelemetryClient = getTelemetryClient(),
|
|
172
|
+
logger = createLogger('prover-client:proving-broker-database'),
|
|
173
|
+
) {
|
|
174
|
+
const epochs: Map<number, SingleEpochDatabase> = new Map<number, SingleEpochDatabase>();
|
|
175
|
+
const files = await readdir(config.dataDirectory!, { recursive: false, withFileTypes: true });
|
|
176
|
+
for (const file of files) {
|
|
177
|
+
if (!file.isDirectory()) {
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
const fullDirectory = join(config.dataDirectory!, file.name);
|
|
181
|
+
const epochDirectory = file.name;
|
|
182
|
+
const epochNumber = parseInt(epochDirectory, 10);
|
|
183
|
+
if (!Number.isSafeInteger(epochNumber) || epochNumber < 0) {
|
|
184
|
+
logger.warn(`Found invalid epoch directory ${fullDirectory} when loading epoch databases, ignoring`);
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
logger.info(
|
|
188
|
+
`Loading broker database for epoch ${epochNumber} from ${fullDirectory} with map size ${config.dataStoreMapSizeKb}KB`,
|
|
189
|
+
);
|
|
190
|
+
const db = await openVersionedStoreAt(
|
|
191
|
+
fullDirectory,
|
|
192
|
+
SingleEpochDatabase.SCHEMA_VERSION,
|
|
193
|
+
config.rollupAddress,
|
|
194
|
+
config.dataStoreMapSizeKb,
|
|
195
|
+
);
|
|
196
|
+
const epochDb = new SingleEpochDatabase(db);
|
|
197
|
+
epochs.set(epochNumber, epochDb);
|
|
198
|
+
}
|
|
199
|
+
const db = new KVBrokerDatabase(epochs, config, client, logger);
|
|
200
|
+
db.start();
|
|
201
|
+
return db;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
private start(): void {
|
|
205
|
+
this.batchQueue.start();
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
async close(): Promise<void> {
|
|
209
|
+
await this.batchQueue.stop();
|
|
210
|
+
for (const [_, v] of this.epochs) {
|
|
211
|
+
await v.close();
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
@trackSpan('KVBrokerDatabase.deleteAllProvingJobsOlderThanEpoch', epochNumber => ({
|
|
216
|
+
[Attributes.EPOCH_NUMBER]: epochNumber,
|
|
217
|
+
}))
|
|
218
|
+
async deleteAllProvingJobsOlderThanEpoch(epochNumber: EpochNumber): Promise<void> {
|
|
219
|
+
const oldEpochs = Array.from(this.epochs.keys()).filter(e => e < Number(epochNumber));
|
|
220
|
+
for (const old of oldEpochs) {
|
|
221
|
+
const db = this.epochs.get(old);
|
|
222
|
+
if (!db) {
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
this.logger.verbose(`Deleting broker database for epoch ${old}`);
|
|
226
|
+
await db.delete();
|
|
227
|
+
this.epochs.delete(old);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Key jobs by the id-derived epoch, the same epoch results are written under (setProvingJobResult et
|
|
232
|
+
// al.). This keeps a job's inputs and its result co-located in one epoch database so both can be read
|
|
233
|
+
// back with a single keyed lookup (getProvingJobInputs/getProvingJobResult) rather than a scan. The id
|
|
234
|
+
// embeds the epoch and equals `job.epochNumber` at every construction site, so this matches the prior
|
|
235
|
+
// storage epoch while making the id the single source of truth for placement.
|
|
236
|
+
addProvingJob(job: ProvingJob): Promise<void> {
|
|
237
|
+
return this.batchQueue.put(job, getEpochFromProvingJobId(job.id));
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
async *allProvingJobs(): AsyncIterableIterator<[ProvingJob, ProvingJobSettledResult | undefined]> {
|
|
241
|
+
const iterators = Array.from(this.epochs.values()).map(x => x.allProvingJobs());
|
|
242
|
+
for (const it of iterators) {
|
|
243
|
+
yield* it;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// A job id is `${epochNumber}:${type}:${inputsHash}`, so its epoch is recoverable directly and maps to
|
|
248
|
+
// exactly one epoch database. Results are already written under this same id-derived epoch (see
|
|
249
|
+
// setProvingJobResult et al.), and a job's `epochNumber` equals its id-epoch at every construction site,
|
|
250
|
+
// so inputs live there too. Use `epochs.get` (not `getEpochDatabase`, which would create the store on a
|
|
251
|
+
// miss) so a read for an unknown id has no side effects.
|
|
252
|
+
getProvingJobInputs(id: ProvingJobId): Promise<ProofUri | undefined> {
|
|
253
|
+
return this.epochs.get(getEpochFromProvingJobId(id))?.getProvingJobInputs(id) ?? Promise.resolve(undefined);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
getProvingJobResult(id: ProvingJobId): Promise<ProvingJobSettledResult | undefined> {
|
|
257
|
+
return this.epochs.get(getEpochFromProvingJobId(id))?.getProvingJobResult(id) ?? Promise.resolve(undefined);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
setProvingJobError(id: ProvingJobId, reason: string): Promise<void> {
|
|
261
|
+
return this.batchQueue.put([id, { status: 'rejected', reason }], getEpochFromProvingJobId(id));
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
setProvingJobAborted(id: ProvingJobId): Promise<void> {
|
|
265
|
+
return this.batchQueue.put([id, { status: 'aborted' }], getEpochFromProvingJobId(id));
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
deleteProvingJobResult(id: ProvingJobId): Promise<void> {
|
|
269
|
+
return this.batchQueue.put([id, undefined], getEpochFromProvingJobId(id));
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
setProvingJobResult(id: ProvingJobId, value: ProofUri): Promise<void> {
|
|
273
|
+
return this.batchQueue.put([id, { status: 'fulfilled', value }], getEpochFromProvingJobId(id));
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
private async getEpochDatabase(epochNumber: EpochNumber): Promise<SingleEpochDatabase> {
|
|
277
|
+
let epochDb = this.epochs.get(epochNumber);
|
|
278
|
+
if (!epochDb) {
|
|
279
|
+
const newEpochDirectory = join(this.config.dataDirectory!, epochNumber.toString());
|
|
280
|
+
await mkdir(newEpochDirectory, { recursive: true });
|
|
281
|
+
this.logger.info(
|
|
282
|
+
`Creating broker database for epoch ${epochNumber} at ${newEpochDirectory} with map size ${this.config.dataStoreMapSizeKb}`,
|
|
283
|
+
);
|
|
284
|
+
const db = await openVersionedStoreAt(
|
|
285
|
+
newEpochDirectory,
|
|
286
|
+
SingleEpochDatabase.SCHEMA_VERSION,
|
|
287
|
+
this.config.rollupAddress,
|
|
288
|
+
this.config.dataStoreMapSizeKb,
|
|
289
|
+
);
|
|
290
|
+
epochDb = new SingleEpochDatabase(db);
|
|
291
|
+
this.epochs.set(epochNumber, epochDb);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
return epochDb;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { EpochNumber } from '@aztec-labs/foundation/branded-types';
|
|
2
|
+
import type { ProofUri, ProvingJob, ProvingJobId, ProvingJobSettledResult } from '@aztec-labs/stdlib/interfaces/server';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* A database for storing proof requests and their results
|
|
6
|
+
*/
|
|
7
|
+
export interface ProvingBrokerDatabase {
|
|
8
|
+
/**
|
|
9
|
+
* Saves a proof request so it can be retrieved later
|
|
10
|
+
* @param job - The proof request to save
|
|
11
|
+
*/
|
|
12
|
+
addProvingJob(job: ProvingJob): Promise<void>;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Deletes all proving jobs belonging to epochs older than the given epoch
|
|
16
|
+
* @param epochNumber - The epoch number beyond which jobs should be deleted
|
|
17
|
+
*/
|
|
18
|
+
deleteAllProvingJobsOlderThanEpoch(epochNumber: EpochNumber): Promise<void>;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Returns an iterator over all saved proving jobs
|
|
22
|
+
*/
|
|
23
|
+
allProvingJobs(): AsyncIterableIterator<[ProvingJob, ProvingJobSettledResult | undefined]>;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Reads a single job's inputs URI by id, or undefined if the job is unknown. The broker keeps only
|
|
27
|
+
* job metadata in memory and reads the (large) inputs from here on demand when dispatching to an agent.
|
|
28
|
+
* @param id - The ID of the proof request
|
|
29
|
+
*/
|
|
30
|
+
getProvingJobInputs(id: ProvingJobId): Promise<ProofUri | undefined>;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Reads a single job's settled result by id, or undefined if not settled. The broker keeps only the
|
|
34
|
+
* settled status in memory and reads the (large) fulfilled proof from here on demand.
|
|
35
|
+
* @param id - The ID of the proof request
|
|
36
|
+
*/
|
|
37
|
+
getProvingJobResult(id: ProvingJobId): Promise<ProvingJobSettledResult | undefined>;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Saves the result of a proof request
|
|
41
|
+
* @param id - The ID of the proof request to save the result for
|
|
42
|
+
* @param ProvingRequestType - The type of proof that was requested
|
|
43
|
+
* @param value - The result of the proof request
|
|
44
|
+
*/
|
|
45
|
+
setProvingJobResult(id: ProvingJobId, value: ProofUri): Promise<void>;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Saves an error that occurred while processing a proof request
|
|
49
|
+
* @param id - The ID of the proof request to save the error for
|
|
50
|
+
* @param ProvingRequestType - The type of proof that was requested
|
|
51
|
+
* @param err - The error that occurred while processing the proof request
|
|
52
|
+
*/
|
|
53
|
+
setProvingJobError(id: ProvingJobId, err: string): Promise<void>;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Records that a proof request was cancelled. Unlike a result or error this is not terminal:
|
|
57
|
+
* re-enqueuing the same job id revives it, so the aborted state can survive a restart without
|
|
58
|
+
* permanently blocking the proof.
|
|
59
|
+
* @param id - The ID of the cancelled proof request
|
|
60
|
+
*/
|
|
61
|
+
setProvingJobAborted(id: ProvingJobId): Promise<void>;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Clears any stored result for a proof request, returning it to the pending state while keeping
|
|
65
|
+
* the job itself. Used when reviving an aborted job so the revival is persisted and a restart
|
|
66
|
+
* cannot resurrect the stale aborted state.
|
|
67
|
+
* @param id - The ID of the proof request whose result should be cleared
|
|
68
|
+
*/
|
|
69
|
+
deleteProvingJobResult(id: ProvingJobId): Promise<void>;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Closes the database
|
|
73
|
+
*/
|
|
74
|
+
close(): Promise<void>;
|
|
75
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import type { Timer } from '@aztec-labs/foundation/timer';
|
|
2
|
+
import { ProvingRequestType } from '@aztec-labs/stdlib/proofs';
|
|
3
|
+
import {
|
|
4
|
+
Attributes,
|
|
5
|
+
type Histogram,
|
|
6
|
+
Metrics,
|
|
7
|
+
type ObservableGauge,
|
|
8
|
+
type ObservableResult,
|
|
9
|
+
type TelemetryClient,
|
|
10
|
+
type UpDownCounter,
|
|
11
|
+
createUpDownCounterWithDefault,
|
|
12
|
+
} from '@aztec-labs/telemetry-client';
|
|
13
|
+
|
|
14
|
+
export type MonitorCallback = (proofType: ProvingRequestType) => number;
|
|
15
|
+
|
|
16
|
+
export class ProvingBrokerInstrumentation {
|
|
17
|
+
private queueSize: ObservableGauge;
|
|
18
|
+
private activeJobs: ObservableGauge;
|
|
19
|
+
private resolvedJobs: UpDownCounter;
|
|
20
|
+
private rejectedJobs: UpDownCounter;
|
|
21
|
+
private abortedJobs: UpDownCounter;
|
|
22
|
+
private timedOutJobs: UpDownCounter;
|
|
23
|
+
private cachedJobs: UpDownCounter;
|
|
24
|
+
private totalJobs: UpDownCounter;
|
|
25
|
+
private jobWait: Histogram;
|
|
26
|
+
private jobDuration: Histogram;
|
|
27
|
+
private retriedJobs: UpDownCounter;
|
|
28
|
+
|
|
29
|
+
constructor(client: TelemetryClient, name = 'ProvingBroker') {
|
|
30
|
+
const meter = client.getMeter(name);
|
|
31
|
+
|
|
32
|
+
this.queueSize = meter.createObservableGauge(Metrics.PROVING_QUEUE_SIZE);
|
|
33
|
+
|
|
34
|
+
this.activeJobs = meter.createObservableGauge(Metrics.PROVING_QUEUE_ACTIVE_JOBS);
|
|
35
|
+
|
|
36
|
+
const provingJobTypes = Object.values(ProvingRequestType).filter(v => typeof v === 'string');
|
|
37
|
+
const provingJobAttrs = { [Attributes.PROVING_JOB_TYPE]: provingJobTypes };
|
|
38
|
+
|
|
39
|
+
this.resolvedJobs = createUpDownCounterWithDefault(meter, Metrics.PROVING_QUEUE_RESOLVED_JOBS, provingJobAttrs);
|
|
40
|
+
|
|
41
|
+
this.rejectedJobs = createUpDownCounterWithDefault(meter, Metrics.PROVING_QUEUE_REJECTED_JOBS, provingJobAttrs);
|
|
42
|
+
|
|
43
|
+
this.abortedJobs = createUpDownCounterWithDefault(meter, Metrics.PROVING_QUEUE_ABORTED_JOBS, provingJobAttrs);
|
|
44
|
+
|
|
45
|
+
this.retriedJobs = createUpDownCounterWithDefault(meter, Metrics.PROVING_QUEUE_RETRIED_JOBS, provingJobAttrs);
|
|
46
|
+
|
|
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);
|
|
52
|
+
|
|
53
|
+
this.jobWait = meter.createHistogram(Metrics.PROVING_QUEUE_JOB_WAIT);
|
|
54
|
+
|
|
55
|
+
this.jobDuration = meter.createHistogram(Metrics.PROVING_QUEUE_JOB_DURATION);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
monitorQueueDepth(fn: MonitorCallback) {
|
|
59
|
+
this.queueSize.addCallback(obs => this.observe(obs, fn));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
monitorActiveJobs(fn: MonitorCallback) {
|
|
63
|
+
this.activeJobs.addCallback(obs => this.observe(obs, fn));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
incResolvedJobs(proofType: ProvingRequestType) {
|
|
67
|
+
this.resolvedJobs.add(1, {
|
|
68
|
+
[Attributes.PROVING_JOB_TYPE]: ProvingRequestType[proofType],
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
incRejectedJobs(proofType: ProvingRequestType) {
|
|
73
|
+
this.rejectedJobs.add(1, {
|
|
74
|
+
[Attributes.PROVING_JOB_TYPE]: ProvingRequestType[proofType],
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
incAbortedJobs(proofType: ProvingRequestType) {
|
|
79
|
+
this.abortedJobs.add(1, {
|
|
80
|
+
[Attributes.PROVING_JOB_TYPE]: ProvingRequestType[proofType],
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
incRetriedJobs(proofType: ProvingRequestType) {
|
|
85
|
+
this.retriedJobs.add(1, {
|
|
86
|
+
[Attributes.PROVING_JOB_TYPE]: ProvingRequestType[proofType],
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
incTimedOutJobs(proofType: ProvingRequestType) {
|
|
91
|
+
this.timedOutJobs.add(1, {
|
|
92
|
+
[Attributes.PROVING_JOB_TYPE]: ProvingRequestType[proofType],
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
incCachedJobs(proofType: ProvingRequestType) {
|
|
97
|
+
this.cachedJobs.add(1, {
|
|
98
|
+
[Attributes.PROVING_JOB_TYPE]: ProvingRequestType[proofType],
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
incTotalJobs(proofType: ProvingRequestType) {
|
|
103
|
+
this.totalJobs.add(1, {
|
|
104
|
+
[Attributes.PROVING_JOB_TYPE]: ProvingRequestType[proofType],
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
recordJobWait(proofType: ProvingRequestType, msOrTimer: Timer | number) {
|
|
109
|
+
const duration = typeof msOrTimer === 'number' ? msOrTimer : Math.floor(msOrTimer.ms());
|
|
110
|
+
this.jobWait.record(duration, {
|
|
111
|
+
[Attributes.PROVING_JOB_TYPE]: ProvingRequestType[proofType],
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
recordJobDuration(proofType: ProvingRequestType, msOrTimer: Timer | number) {
|
|
116
|
+
const duration = typeof msOrTimer === 'number' ? msOrTimer : Math.floor(msOrTimer.ms());
|
|
117
|
+
this.jobDuration.record(duration, {
|
|
118
|
+
[Attributes.PROVING_JOB_TYPE]: ProvingRequestType[proofType],
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
private observe(obs: ObservableResult, fn: MonitorCallback) {
|
|
123
|
+
for (const proofType of Object.values(ProvingRequestType)) {
|
|
124
|
+
// a type predicate for TypeScript to recognize that we're only iterating over enum values
|
|
125
|
+
if (typeof proofType !== 'number') {
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
obs.observe(fn(proofType), {
|
|
129
|
+
[Attributes.PROVING_JOB_TYPE]: ProvingRequestType[proofType],
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|