@aztec/prover-node 0.0.0-test.1 → 0.0.1-fake-ceab37513c
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/actions/download-epoch-proving-job.d.ts +18 -0
- package/dest/actions/download-epoch-proving-job.d.ts.map +1 -0
- package/dest/actions/download-epoch-proving-job.js +37 -0
- package/dest/actions/index.d.ts +3 -0
- package/dest/actions/index.d.ts.map +1 -0
- package/dest/actions/index.js +2 -0
- package/dest/actions/rerun-epoch-proving-job.d.ts +11 -0
- package/dest/actions/rerun-epoch-proving-job.d.ts.map +1 -0
- package/dest/actions/rerun-epoch-proving-job.js +40 -0
- package/dest/actions/upload-epoch-proof-failure.d.ts +15 -0
- package/dest/actions/upload-epoch-proof-failure.d.ts.map +1 -0
- package/dest/actions/upload-epoch-proof-failure.js +78 -0
- package/dest/bin/run-failed-epoch.d.ts +2 -0
- package/dest/bin/run-failed-epoch.d.ts.map +1 -0
- package/dest/bin/run-failed-epoch.js +67 -0
- package/dest/config.d.ts +12 -9
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +81 -14
- package/dest/factory.d.ts +12 -8
- package/dest/factory.d.ts.map +1 -1
- package/dest/factory.js +94 -31
- package/dest/index.d.ts +1 -1
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +1 -1
- package/dest/job/epoch-proving-job-data.d.ts +16 -0
- package/dest/job/epoch-proving-job-data.d.ts.map +1 -0
- package/dest/job/epoch-proving-job-data.js +52 -0
- package/dest/job/epoch-proving-job.d.ts +30 -15
- package/dest/job/epoch-proving-job.d.ts.map +1 -1
- package/dest/job/epoch-proving-job.js +119 -46
- package/dest/metrics.d.ts +28 -4
- package/dest/metrics.d.ts.map +1 -1
- package/dest/metrics.js +141 -35
- package/dest/monitors/epoch-monitor.d.ts +3 -1
- package/dest/monitors/epoch-monitor.d.ts.map +1 -1
- package/dest/monitors/epoch-monitor.js +15 -2
- package/dest/prover-node-publisher.d.ts +7 -10
- package/dest/prover-node-publisher.d.ts.map +1 -1
- package/dest/prover-node-publisher.js +58 -59
- package/dest/prover-node.d.ts +43 -39
- package/dest/prover-node.d.ts.map +1 -1
- package/dest/prover-node.js +171 -100
- package/dest/prover-publisher-factory.d.ts +21 -0
- package/dest/prover-publisher-factory.d.ts.map +1 -0
- package/dest/prover-publisher-factory.js +26 -0
- package/dest/test/index.d.ts +4 -2
- package/dest/test/index.d.ts.map +1 -1
- package/dest/test/index.js +1 -3
- package/package.json +36 -31
- package/src/actions/download-epoch-proving-job.ts +44 -0
- package/src/actions/index.ts +2 -0
- package/src/actions/rerun-epoch-proving-job.ts +61 -0
- package/src/actions/upload-epoch-proof-failure.ts +88 -0
- package/src/bin/run-failed-epoch.ts +77 -0
- package/src/config.ts +108 -24
- package/src/factory.ts +158 -43
- package/src/index.ts +1 -1
- package/src/job/epoch-proving-job-data.ts +76 -0
- package/src/job/epoch-proving-job.ts +177 -46
- package/src/metrics.ts +135 -37
- package/src/monitors/epoch-monitor.ts +16 -5
- package/src/prover-node-publisher.ts +90 -85
- package/src/prover-node.ts +203 -126
- package/src/prover-publisher-factory.ts +37 -0
- package/src/test/index.ts +7 -4
- package/dest/http.d.ts +0 -8
- package/dest/http.d.ts.map +0 -1
- package/dest/http.js +0 -9
- package/dest/prover-coordination/config.d.ts +0 -7
- package/dest/prover-coordination/config.d.ts.map +0 -1
- package/dest/prover-coordination/config.js +0 -11
- package/dest/prover-coordination/factory.d.ts +0 -22
- package/dest/prover-coordination/factory.d.ts.map +0 -1
- package/dest/prover-coordination/factory.js +0 -42
- package/dest/prover-coordination/index.d.ts +0 -3
- package/dest/prover-coordination/index.d.ts.map +0 -1
- package/dest/prover-coordination/index.js +0 -2
- package/src/http.ts +0 -13
- package/src/prover-coordination/config.ts +0 -17
- package/src/prover-coordination/factory.ts +0 -72
- package/src/prover-coordination/index.ts +0 -2
|
@@ -4,57 +4,55 @@ function _ts_decorate(decorators, target, key, desc) {
|
|
|
4
4
|
else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
5
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
6
|
}
|
|
7
|
+
import { BatchedBlob, Blob } from '@aztec/blob-lib';
|
|
8
|
+
import { NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP } from '@aztec/constants';
|
|
7
9
|
import { asyncPool } from '@aztec/foundation/async-pool';
|
|
10
|
+
import { padArrayEnd } from '@aztec/foundation/collection';
|
|
11
|
+
import { Fr } from '@aztec/foundation/fields';
|
|
8
12
|
import { createLogger } from '@aztec/foundation/log';
|
|
9
|
-
import { promiseWithResolvers } from '@aztec/foundation/promise';
|
|
13
|
+
import { RunningPromise, promiseWithResolvers } from '@aztec/foundation/promise';
|
|
10
14
|
import { Timer } from '@aztec/foundation/timer';
|
|
11
15
|
import { EpochProvingJobTerminalState } from '@aztec/stdlib/interfaces/server';
|
|
16
|
+
import { MerkleTreeId } from '@aztec/stdlib/trees';
|
|
12
17
|
import { Attributes, trackSpan } from '@aztec/telemetry-client';
|
|
13
18
|
import * as crypto from 'node:crypto';
|
|
19
|
+
import { validateEpochProvingJobData } from './epoch-proving-job-data.js';
|
|
14
20
|
/**
|
|
15
|
-
* Job that grabs a range of blocks from the
|
|
21
|
+
* Job that grabs a range of blocks from the unfinalized chain from L1, gets their txs given their hashes,
|
|
16
22
|
* re-executes their public calls, generates a rollup proof, and submits it to L1. This job will update the
|
|
17
23
|
* world state as part of public call execution via the public processor.
|
|
18
24
|
*/ export class EpochProvingJob {
|
|
25
|
+
data;
|
|
19
26
|
dbProvider;
|
|
20
|
-
epochNumber;
|
|
21
|
-
blocks;
|
|
22
|
-
txs;
|
|
23
27
|
prover;
|
|
24
28
|
publicProcessorFactory;
|
|
25
29
|
publisher;
|
|
26
30
|
l2BlockSource;
|
|
27
|
-
l1ToL2MessageSource;
|
|
28
31
|
metrics;
|
|
29
32
|
deadline;
|
|
30
33
|
config;
|
|
31
|
-
cleanUp;
|
|
32
34
|
state;
|
|
33
35
|
log;
|
|
34
36
|
uuid;
|
|
35
37
|
runPromise;
|
|
38
|
+
epochCheckPromise;
|
|
36
39
|
deadlineTimeoutHandler;
|
|
37
40
|
tracer;
|
|
38
|
-
constructor(
|
|
39
|
-
|
|
40
|
-
}, cleanUp = ()=>Promise.resolve()){
|
|
41
|
+
constructor(data, dbProvider, prover, publicProcessorFactory, publisher, l2BlockSource, metrics, deadline, config){
|
|
42
|
+
this.data = data;
|
|
41
43
|
this.dbProvider = dbProvider;
|
|
42
|
-
this.epochNumber = epochNumber;
|
|
43
|
-
this.blocks = blocks;
|
|
44
|
-
this.txs = txs;
|
|
45
44
|
this.prover = prover;
|
|
46
45
|
this.publicProcessorFactory = publicProcessorFactory;
|
|
47
46
|
this.publisher = publisher;
|
|
48
47
|
this.l2BlockSource = l2BlockSource;
|
|
49
|
-
this.l1ToL2MessageSource = l1ToL2MessageSource;
|
|
50
48
|
this.metrics = metrics;
|
|
51
49
|
this.deadline = deadline;
|
|
52
50
|
this.config = config;
|
|
53
|
-
this.cleanUp = cleanUp;
|
|
54
51
|
this.state = 'initialized';
|
|
55
52
|
this.log = createLogger('prover-node:epoch-proving-job');
|
|
53
|
+
validateEpochProvingJobData(data);
|
|
56
54
|
this.uuid = crypto.randomUUID();
|
|
57
|
-
this.tracer = metrics.
|
|
55
|
+
this.tracer = metrics.tracer;
|
|
58
56
|
}
|
|
59
57
|
getId() {
|
|
60
58
|
return this.uuid;
|
|
@@ -63,12 +61,34 @@ import * as crypto from 'node:crypto';
|
|
|
63
61
|
return this.state;
|
|
64
62
|
}
|
|
65
63
|
getEpochNumber() {
|
|
66
|
-
return this.epochNumber;
|
|
64
|
+
return this.data.epochNumber;
|
|
65
|
+
}
|
|
66
|
+
getDeadline() {
|
|
67
|
+
return this.deadline;
|
|
68
|
+
}
|
|
69
|
+
getProvingData() {
|
|
70
|
+
return this.data;
|
|
71
|
+
}
|
|
72
|
+
get epochNumber() {
|
|
73
|
+
return this.data.epochNumber;
|
|
74
|
+
}
|
|
75
|
+
get blocks() {
|
|
76
|
+
return this.data.blocks;
|
|
77
|
+
}
|
|
78
|
+
get txs() {
|
|
79
|
+
return this.data.txs;
|
|
80
|
+
}
|
|
81
|
+
get attestations() {
|
|
82
|
+
return this.data.attestations;
|
|
67
83
|
}
|
|
68
84
|
/**
|
|
69
85
|
* Proves the given epoch and submits the proof to L1.
|
|
70
86
|
*/ async run() {
|
|
71
87
|
this.scheduleDeadlineStop();
|
|
88
|
+
if (!this.config.skipEpochCheck) {
|
|
89
|
+
await this.scheduleEpochCheck();
|
|
90
|
+
}
|
|
91
|
+
const attestations = this.attestations.map((attestation)=>attestation.toViem());
|
|
72
92
|
const epochNumber = Number(this.epochNumber);
|
|
73
93
|
const epochSizeBlocks = this.blocks.length;
|
|
74
94
|
const epochSizeTxs = this.blocks.reduce((total, current)=>total + current.body.txEffects.length, 0);
|
|
@@ -88,14 +108,16 @@ import * as crypto from 'node:crypto';
|
|
|
88
108
|
const { promise, resolve } = promiseWithResolvers();
|
|
89
109
|
this.runPromise = promise;
|
|
90
110
|
try {
|
|
91
|
-
this.
|
|
92
|
-
await
|
|
93
|
-
|
|
111
|
+
const allBlobs = (await Promise.all(this.blocks.map(async (block)=>await Blob.getBlobsPerBlock(block.body.toBlobFields())))).flat();
|
|
112
|
+
const finalBlobBatchingChallenges = await BatchedBlob.precomputeBatchedBlobChallenges(allBlobs);
|
|
113
|
+
this.prover.startNewEpoch(epochNumber, fromBlock, epochSizeBlocks, finalBlobBatchingChallenges);
|
|
114
|
+
await this.prover.startTubeCircuits(Array.from(this.txs.values()));
|
|
115
|
+
await asyncPool(this.config.parallelBlockLimit ?? 32, this.blocks, async (block)=>{
|
|
94
116
|
this.checkState();
|
|
95
117
|
const globalVariables = block.header.globalVariables;
|
|
96
|
-
const txs =
|
|
97
|
-
const l1ToL2Messages =
|
|
98
|
-
const previousHeader =
|
|
118
|
+
const txs = this.getTxs(block);
|
|
119
|
+
const l1ToL2Messages = this.getL1ToL2Messages(block);
|
|
120
|
+
const previousHeader = this.getBlockHeader(block.number - 1);
|
|
99
121
|
this.log.verbose(`Starting processing block ${block.number}`, {
|
|
100
122
|
number: block.number,
|
|
101
123
|
blockHash: (await block.hash()).toString(),
|
|
@@ -110,7 +132,7 @@ import * as crypto from 'node:crypto';
|
|
|
110
132
|
// Start block proving
|
|
111
133
|
await this.prover.startNewBlock(globalVariables, l1ToL2Messages, previousHeader);
|
|
112
134
|
// Process public fns
|
|
113
|
-
const db = await this.
|
|
135
|
+
const db = await this.createFork(block.number - 1, l1ToL2Messages);
|
|
114
136
|
const publicProcessor = this.publicProcessorFactory.create(db, globalVariables, true);
|
|
115
137
|
const processed = await this.processTxs(publicProcessor, txs);
|
|
116
138
|
await this.prover.addTxs(processed);
|
|
@@ -125,19 +147,27 @@ import * as crypto from 'node:crypto';
|
|
|
125
147
|
});
|
|
126
148
|
const executionTime = timer.ms();
|
|
127
149
|
this.progressState('awaiting-prover');
|
|
128
|
-
const { publicInputs, proof } = await this.prover.
|
|
129
|
-
this.log.info(`
|
|
150
|
+
const { publicInputs, proof, batchedBlobInputs } = await this.prover.finalizeEpoch();
|
|
151
|
+
this.log.info(`Finalized proof for epoch ${epochNumber}`, {
|
|
130
152
|
epochNumber,
|
|
131
153
|
uuid: this.uuid,
|
|
132
154
|
duration: timer.ms()
|
|
133
155
|
});
|
|
134
156
|
this.progressState('publishing-proof');
|
|
157
|
+
if (this.config.skipSubmitProof) {
|
|
158
|
+
this.log.info(`Proof publishing is disabled. Dropping valid proof for epoch ${epochNumber} (blocks ${fromBlock} to ${toBlock})`);
|
|
159
|
+
this.state = 'completed';
|
|
160
|
+
this.metrics.recordProvingJob(executionTime, timer.ms(), epochSizeBlocks, epochSizeTxs);
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
135
163
|
const success = await this.publisher.submitEpochProof({
|
|
136
164
|
fromBlock,
|
|
137
165
|
toBlock,
|
|
138
166
|
epochNumber,
|
|
139
167
|
publicInputs,
|
|
140
|
-
proof
|
|
168
|
+
proof,
|
|
169
|
+
batchedBlobInputs,
|
|
170
|
+
attestations
|
|
141
171
|
});
|
|
142
172
|
if (!success) {
|
|
143
173
|
throw new Error('Failed to submit epoch proof to L1');
|
|
@@ -152,7 +182,8 @@ import * as crypto from 'node:crypto';
|
|
|
152
182
|
if (err && err.name === 'HaltExecutionError') {
|
|
153
183
|
this.log.warn(`Halted execution of epoch ${epochNumber} prover job`, {
|
|
154
184
|
uuid: this.uuid,
|
|
155
|
-
epochNumber
|
|
185
|
+
epochNumber,
|
|
186
|
+
details: err.message
|
|
156
187
|
});
|
|
157
188
|
return;
|
|
158
189
|
}
|
|
@@ -160,20 +191,35 @@ import * as crypto from 'node:crypto';
|
|
|
160
191
|
uuid: this.uuid,
|
|
161
192
|
epochNumber
|
|
162
193
|
});
|
|
163
|
-
this.state
|
|
194
|
+
if (this.state === 'processing' || this.state === 'awaiting-prover' || this.state === 'publishing-proof') {
|
|
195
|
+
this.state = 'failed';
|
|
196
|
+
}
|
|
164
197
|
} finally{
|
|
165
198
|
clearTimeout(this.deadlineTimeoutHandler);
|
|
166
|
-
await this.
|
|
199
|
+
await this.epochCheckPromise?.stop();
|
|
167
200
|
await this.prover.stop();
|
|
168
201
|
resolve();
|
|
169
202
|
}
|
|
170
203
|
}
|
|
204
|
+
/**
|
|
205
|
+
* Create a new db fork for tx processing, inserting all L1 to L2.
|
|
206
|
+
* REFACTOR: The prover already spawns a db fork of its own for each block, so we may be able to do away with just one fork.
|
|
207
|
+
*/ async createFork(blockNumber, l1ToL2Messages) {
|
|
208
|
+
const db = await this.dbProvider.fork(blockNumber);
|
|
209
|
+
const l1ToL2MessagesPadded = padArrayEnd(l1ToL2Messages, Fr.ZERO, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, 'Too many L1 to L2 messages');
|
|
210
|
+
this.log.verbose(`Creating fork at ${blockNumber} with ${l1ToL2Messages.length} L1 to L2 messages`, {
|
|
211
|
+
blockNumber,
|
|
212
|
+
l1ToL2Messages: l1ToL2MessagesPadded.map((m)=>m.toString())
|
|
213
|
+
});
|
|
214
|
+
await db.appendLeaves(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, l1ToL2MessagesPadded);
|
|
215
|
+
return db;
|
|
216
|
+
}
|
|
171
217
|
progressState(state) {
|
|
172
218
|
this.checkState();
|
|
173
219
|
this.state = state;
|
|
174
220
|
}
|
|
175
221
|
checkState() {
|
|
176
|
-
if (this.state === 'timed-out' || this.state === 'stopped' || this.state === 'failed') {
|
|
222
|
+
if (this.state === 'timed-out' || this.state === 'stopped' || this.state === 'failed' || this.state === 'reorg') {
|
|
177
223
|
throw new HaltExecutionError(this.state);
|
|
178
224
|
}
|
|
179
225
|
}
|
|
@@ -209,22 +255,47 @@ import * as crypto from 'node:crypto';
|
|
|
209
255
|
}, timeout);
|
|
210
256
|
}
|
|
211
257
|
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
258
|
+
/**
|
|
259
|
+
* Kicks off a running promise that queries the archiver for the set of L2 blocks of the current epoch.
|
|
260
|
+
* If those change, stops the proving job with a `rerun` state, so the node re-enqueues it.
|
|
261
|
+
*/ async scheduleEpochCheck() {
|
|
262
|
+
const l2BlockSource = this.l2BlockSource;
|
|
263
|
+
if (!l2BlockSource) {
|
|
264
|
+
this.log.warn(`No L2 block source available, skipping epoch check`);
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
const intervalMs = Math.ceil((await l2BlockSource.getL1Constants()).ethereumSlotDuration / 2) * 1000;
|
|
268
|
+
this.epochCheckPromise = new RunningPromise(async ()=>{
|
|
269
|
+
const blocks = await l2BlockSource.getBlockHeadersForEpoch(this.epochNumber);
|
|
270
|
+
const blockHashes = await Promise.all(blocks.map((block)=>block.hash()));
|
|
271
|
+
const thisBlockHashes = await Promise.all(this.blocks.map((block)=>block.hash()));
|
|
272
|
+
if (blocks.length !== this.blocks.length || !blockHashes.every((block, i)=>block.equals(thisBlockHashes[i]))) {
|
|
273
|
+
this.log.warn('Epoch blocks changed underfoot', {
|
|
274
|
+
uuid: this.uuid,
|
|
275
|
+
epochNumber: this.epochNumber,
|
|
276
|
+
oldBlockHashes: thisBlockHashes,
|
|
277
|
+
newBlockHashes: blockHashes
|
|
278
|
+
});
|
|
279
|
+
void this.stop('reorg');
|
|
280
|
+
}
|
|
281
|
+
}, this.log, intervalMs).start();
|
|
282
|
+
this.log.verbose(`Scheduled epoch check for epoch ${this.epochNumber} every ${intervalMs}ms`);
|
|
283
|
+
}
|
|
284
|
+
/* Returns the header for the given block number based on the epoch proving job data. */ getBlockHeader(blockNumber) {
|
|
285
|
+
const block = this.blocks.find((b)=>b.number === blockNumber);
|
|
286
|
+
if (block) {
|
|
287
|
+
return block.header;
|
|
215
288
|
}
|
|
216
|
-
|
|
289
|
+
if (blockNumber === Number(this.data.previousBlockHeader.getBlockNumber())) {
|
|
290
|
+
return this.data.previousBlockHeader;
|
|
291
|
+
}
|
|
292
|
+
throw new Error(`Block header not found for block number ${blockNumber} (got ${this.blocks.map((b)=>b.number).join(', ')} and previous header ${this.data.previousBlockHeader.getBlockNumber()})`);
|
|
217
293
|
}
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
const txsAndHashes = await Promise.all(this.txs.map(async (tx)=>({
|
|
221
|
-
tx,
|
|
222
|
-
hash: await tx.getTxHash()
|
|
223
|
-
})));
|
|
224
|
-
return txsAndHashes.filter((txAndHash)=>txHashes.includes(txAndHash.hash.toBigInt())).map((txAndHash)=>txAndHash.tx);
|
|
294
|
+
getTxs(block) {
|
|
295
|
+
return block.body.txEffects.map((txEffect)=>this.txs.get(txEffect.txHash.toString()));
|
|
225
296
|
}
|
|
226
297
|
getL1ToL2Messages(block) {
|
|
227
|
-
return this.
|
|
298
|
+
return this.data.l1ToL2Messages[block.number];
|
|
228
299
|
}
|
|
229
300
|
async processTxs(publicProcessor, txs) {
|
|
230
301
|
const { deadline } = this;
|
|
@@ -232,7 +303,8 @@ import * as crypto from 'node:crypto';
|
|
|
232
303
|
deadline
|
|
233
304
|
});
|
|
234
305
|
if (failedTxs.length) {
|
|
235
|
-
|
|
306
|
+
const failedTxHashes = await Promise.all(failedTxs.map(({ tx })=>tx.getTxHash()));
|
|
307
|
+
throw new Error(`Txs failed processing: ${failedTxs.map(({ error }, index)=>`${failedTxHashes[index]} (${error})`).join(', ')}`);
|
|
236
308
|
}
|
|
237
309
|
if (processedTxs.length !== txs.length) {
|
|
238
310
|
throw new Error(`Failed to process all txs: processed ${processedTxs.length} out of ${txs.length}`);
|
|
@@ -243,13 +315,14 @@ import * as crypto from 'node:crypto';
|
|
|
243
315
|
_ts_decorate([
|
|
244
316
|
trackSpan('EpochProvingJob.run', function() {
|
|
245
317
|
return {
|
|
246
|
-
[Attributes.EPOCH_NUMBER]: Number(this.epochNumber)
|
|
318
|
+
[Attributes.EPOCH_NUMBER]: Number(this.data.epochNumber)
|
|
247
319
|
};
|
|
248
320
|
})
|
|
249
321
|
], EpochProvingJob.prototype, "run", null);
|
|
250
322
|
class HaltExecutionError extends Error {
|
|
323
|
+
state;
|
|
251
324
|
constructor(state){
|
|
252
|
-
super(`Halted execution due to state ${state}`);
|
|
325
|
+
super(`Halted execution due to state ${state}`), this.state = state;
|
|
253
326
|
this.name = 'HaltExecutionError';
|
|
254
327
|
}
|
|
255
328
|
}
|
package/dest/metrics.d.ts
CHANGED
|
@@ -1,12 +1,35 @@
|
|
|
1
|
+
import type { RollupContract } from '@aztec/ethereum';
|
|
2
|
+
import type { EthAddress } from '@aztec/foundation/eth-address';
|
|
1
3
|
import type { L1PublishProofStats } from '@aztec/stdlib/stats';
|
|
2
|
-
import { type Gauge, type Histogram, type TelemetryClient, type UpDownCounter } from '@aztec/telemetry-client';
|
|
3
|
-
export declare class
|
|
4
|
-
|
|
4
|
+
import { type Gauge, type Histogram, type Meter, type TelemetryClient, type Tracer, type UpDownCounter } from '@aztec/telemetry-client';
|
|
5
|
+
export declare class ProverNodeJobMetrics {
|
|
6
|
+
private meter;
|
|
7
|
+
readonly tracer: Tracer;
|
|
5
8
|
private logger;
|
|
6
9
|
proverEpochExecutionDuration: Histogram;
|
|
7
10
|
provingJobDuration: Histogram;
|
|
8
11
|
provingJobBlocks: Gauge;
|
|
9
12
|
provingJobTransactions: Gauge;
|
|
13
|
+
constructor(meter: Meter, tracer: Tracer, logger?: import("@aztec/foundation/log").Logger);
|
|
14
|
+
recordProvingJob(executionTimeMs: number, totalTimeMs: number, numBlocks: number, numTxs: number): void;
|
|
15
|
+
}
|
|
16
|
+
export declare class ProverNodeRewardsMetrics {
|
|
17
|
+
private meter;
|
|
18
|
+
private coinbase;
|
|
19
|
+
private rollup;
|
|
20
|
+
private logger;
|
|
21
|
+
private rewards;
|
|
22
|
+
private accumulatedRewards;
|
|
23
|
+
private prevEpoch;
|
|
24
|
+
private proofSubmissionEpochs;
|
|
25
|
+
constructor(meter: Meter, coinbase: EthAddress, rollup: RollupContract, logger?: import("@aztec/foundation/log").Logger);
|
|
26
|
+
start(): Promise<void>;
|
|
27
|
+
stop(): void;
|
|
28
|
+
private observe;
|
|
29
|
+
}
|
|
30
|
+
export declare class ProverNodePublisherMetrics {
|
|
31
|
+
readonly client: TelemetryClient;
|
|
32
|
+
private logger;
|
|
10
33
|
gasPrice: Histogram;
|
|
11
34
|
txCount: UpDownCounter;
|
|
12
35
|
txDuration: Histogram;
|
|
@@ -15,11 +38,12 @@ export declare class ProverNodeMetrics {
|
|
|
15
38
|
txCalldataGas: Histogram;
|
|
16
39
|
txBlobDataGasUsed: Histogram;
|
|
17
40
|
txBlobDataGasCost: Histogram;
|
|
41
|
+
txTotalFee: Histogram;
|
|
18
42
|
private senderBalance;
|
|
43
|
+
private meter;
|
|
19
44
|
constructor(client: TelemetryClient, name?: string, logger?: import("@aztec/foundation/log").Logger);
|
|
20
45
|
recordFailedTx(): void;
|
|
21
46
|
recordSubmitProof(durationMs: number, stats: L1PublishProofStats): void;
|
|
22
|
-
recordProvingJob(executionTimeMs: number, totalTimeMs: number, numBlocks: number, numTxs: number): void;
|
|
23
47
|
recordSenderBalance(wei: bigint, senderAddress: string): void;
|
|
24
48
|
private recordTx;
|
|
25
49
|
}
|
package/dest/metrics.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"metrics.d.ts","sourceRoot":"","sources":["../src/metrics.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"metrics.d.ts","sourceRoot":"","sources":["../src/metrics.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAEhE,OAAO,KAAK,EAAE,mBAAmB,EAAkB,MAAM,qBAAqB,CAAC;AAC/E,OAAO,EAGL,KAAK,KAAK,EACV,KAAK,SAAS,EACd,KAAK,KAAK,EAGV,KAAK,eAAe,EACpB,KAAK,MAAM,EACX,KAAK,aAAa,EAEnB,MAAM,yBAAyB,CAAC;AAIjC,qBAAa,oBAAoB;IAO7B,OAAO,CAAC,KAAK;aACG,MAAM,EAAE,MAAM;IAC9B,OAAO,CAAC,MAAM;IARhB,4BAA4B,EAAE,SAAS,CAAC;IACxC,kBAAkB,EAAE,SAAS,CAAC;IAC9B,gBAAgB,EAAE,KAAK,CAAC;IACxB,sBAAsB,EAAE,KAAK,CAAC;gBAGpB,KAAK,EAAE,KAAK,EACJ,MAAM,EAAE,MAAM,EACtB,MAAM,yCAAgD;IAsBzD,gBAAgB,CAAC,eAAe,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;CAMxG;AAED,qBAAa,wBAAwB;IAOjC,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,MAAM;IAThB,OAAO,CAAC,OAAO,CAAkB;IACjC,OAAO,CAAC,kBAAkB,CAAgB;IAC1C,OAAO,CAAC,SAAS,CAAO;IACxB,OAAO,CAAC,qBAAqB,CAAK;gBAGxB,KAAK,EAAE,KAAK,EACZ,QAAQ,EAAE,UAAU,EACpB,MAAM,EAAE,cAAc,EACtB,MAAM,yCAAgD;IAanD,KAAK;IAMX,IAAI;IAIX,OAAO,CAAC,OAAO,CAwBb;CACH;AAED,qBAAa,0BAA0B;aAenB,MAAM,EAAE,eAAe;IAEvC,OAAO,CAAC,MAAM;IAhBhB,QAAQ,EAAE,SAAS,CAAC;IACpB,OAAO,EAAE,aAAa,CAAC;IACvB,UAAU,EAAE,SAAS,CAAC;IACtB,KAAK,EAAE,SAAS,CAAC;IACjB,cAAc,EAAE,SAAS,CAAC;IAC1B,aAAa,EAAE,SAAS,CAAC;IACzB,iBAAiB,EAAE,SAAS,CAAC;IAC7B,iBAAiB,EAAE,SAAS,CAAC;IAC7B,UAAU,EAAE,SAAS,CAAC;IAEtB,OAAO,CAAC,aAAa,CAAQ;IAC7B,OAAO,CAAC,KAAK,CAAQ;gBAGH,MAAM,EAAE,eAAe,EACvC,IAAI,SAAe,EACX,MAAM,yCAAgD;IAoEhE,cAAc;IAOd,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,mBAAmB;IAIzD,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM;IAO7D,OAAO,CAAC,QAAQ;CAuCjB"}
|
package/dest/metrics.js
CHANGED
|
@@ -1,83 +1,187 @@
|
|
|
1
1
|
import { createLogger } from '@aztec/foundation/log';
|
|
2
2
|
import { Attributes, Metrics, ValueType } from '@aztec/telemetry-client';
|
|
3
|
-
import { formatEther } from 'viem';
|
|
4
|
-
export class
|
|
5
|
-
|
|
3
|
+
import { formatEther, formatUnits } from 'viem';
|
|
4
|
+
export class ProverNodeJobMetrics {
|
|
5
|
+
meter;
|
|
6
|
+
tracer;
|
|
6
7
|
logger;
|
|
7
8
|
proverEpochExecutionDuration;
|
|
8
9
|
provingJobDuration;
|
|
9
10
|
provingJobBlocks;
|
|
10
11
|
provingJobTransactions;
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
txGas;
|
|
15
|
-
txCalldataSize;
|
|
16
|
-
txCalldataGas;
|
|
17
|
-
txBlobDataGasUsed;
|
|
18
|
-
txBlobDataGasCost;
|
|
19
|
-
senderBalance;
|
|
20
|
-
constructor(client, name = 'ProverNode', logger = createLogger('prover-node:publisher:metrics')){
|
|
21
|
-
this.client = client;
|
|
12
|
+
constructor(meter, tracer, logger = createLogger('prover-node:publisher:metrics')){
|
|
13
|
+
this.meter = meter;
|
|
14
|
+
this.tracer = tracer;
|
|
22
15
|
this.logger = logger;
|
|
23
|
-
|
|
24
|
-
this.proverEpochExecutionDuration = meter.createHistogram(Metrics.PROVER_NODE_EXECUTION_DURATION, {
|
|
16
|
+
this.proverEpochExecutionDuration = this.meter.createHistogram(Metrics.PROVER_NODE_EXECUTION_DURATION, {
|
|
25
17
|
description: 'Duration of execution of an epoch by the prover',
|
|
26
18
|
unit: 'ms',
|
|
27
19
|
valueType: ValueType.INT
|
|
28
20
|
});
|
|
29
|
-
this.provingJobDuration = meter.createHistogram(Metrics.PROVER_NODE_JOB_DURATION, {
|
|
21
|
+
this.provingJobDuration = this.meter.createHistogram(Metrics.PROVER_NODE_JOB_DURATION, {
|
|
30
22
|
description: 'Duration of proving job',
|
|
31
23
|
unit: 's',
|
|
32
24
|
valueType: ValueType.DOUBLE
|
|
33
25
|
});
|
|
34
|
-
this.provingJobBlocks = meter.createGauge(Metrics.PROVER_NODE_JOB_BLOCKS, {
|
|
26
|
+
this.provingJobBlocks = this.meter.createGauge(Metrics.PROVER_NODE_JOB_BLOCKS, {
|
|
35
27
|
description: 'Number of blocks in a proven epoch',
|
|
36
28
|
valueType: ValueType.INT
|
|
37
29
|
});
|
|
38
|
-
this.provingJobTransactions = meter.createGauge(Metrics.PROVER_NODE_JOB_TRANSACTIONS, {
|
|
30
|
+
this.provingJobTransactions = this.meter.createGauge(Metrics.PROVER_NODE_JOB_TRANSACTIONS, {
|
|
39
31
|
description: 'Number of transactions in a proven epoch',
|
|
40
32
|
valueType: ValueType.INT
|
|
41
33
|
});
|
|
42
|
-
|
|
34
|
+
}
|
|
35
|
+
recordProvingJob(executionTimeMs, totalTimeMs, numBlocks, numTxs) {
|
|
36
|
+
this.proverEpochExecutionDuration.record(Math.ceil(executionTimeMs));
|
|
37
|
+
this.provingJobDuration.record(totalTimeMs / 1000);
|
|
38
|
+
this.provingJobBlocks.record(Math.floor(numBlocks));
|
|
39
|
+
this.provingJobTransactions.record(Math.floor(numTxs));
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
export class ProverNodeRewardsMetrics {
|
|
43
|
+
meter;
|
|
44
|
+
coinbase;
|
|
45
|
+
rollup;
|
|
46
|
+
logger;
|
|
47
|
+
rewards;
|
|
48
|
+
accumulatedRewards;
|
|
49
|
+
prevEpoch;
|
|
50
|
+
proofSubmissionEpochs;
|
|
51
|
+
constructor(meter, coinbase, rollup, logger = createLogger('prover-node:publisher:metrics')){
|
|
52
|
+
this.meter = meter;
|
|
53
|
+
this.coinbase = coinbase;
|
|
54
|
+
this.rollup = rollup;
|
|
55
|
+
this.logger = logger;
|
|
56
|
+
this.prevEpoch = -1n;
|
|
57
|
+
this.proofSubmissionEpochs = 0;
|
|
58
|
+
this.observe = async (observer)=>{
|
|
59
|
+
const epoch = await this.rollup.getCurrentEpochNumber();
|
|
60
|
+
if (epoch > this.proofSubmissionEpochs) {
|
|
61
|
+
// look at the prev epoch so that we get an accurate value, after proof submission window has closed
|
|
62
|
+
// For example, if proof submission window is 1 epoch, and we are in epoch 2, we should be looking at epoch 0.
|
|
63
|
+
// Similarly, if the proof submission window is 0, and we are in epoch 1, we should be looking at epoch 0.
|
|
64
|
+
const closedEpoch = epoch - BigInt(this.proofSubmissionEpochs) - 1n;
|
|
65
|
+
const rewards = await this.rollup.getSpecificProverRewardsForEpoch(closedEpoch, this.coinbase);
|
|
66
|
+
const fmt = parseFloat(formatUnits(rewards, 18));
|
|
67
|
+
observer.observe(this.rewards, fmt, {
|
|
68
|
+
[Attributes.COINBASE]: this.coinbase.toString()
|
|
69
|
+
});
|
|
70
|
+
// only accumulate once per epoch
|
|
71
|
+
if (closedEpoch > this.prevEpoch) {
|
|
72
|
+
this.prevEpoch = closedEpoch;
|
|
73
|
+
this.accumulatedRewards.add(fmt, {
|
|
74
|
+
[Attributes.COINBASE]: this.coinbase.toString()
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
this.rewards = this.meter.createObservableGauge(Metrics.PROVER_NODE_REWARDS_PER_EPOCH, {
|
|
80
|
+
valueType: ValueType.DOUBLE,
|
|
81
|
+
description: 'The rewards earned'
|
|
82
|
+
});
|
|
83
|
+
this.accumulatedRewards = this.meter.createUpDownCounter(Metrics.PROVER_NODE_REWARDS_TOTAL, {
|
|
84
|
+
valueType: ValueType.DOUBLE,
|
|
85
|
+
description: 'The rewards earned (total)'
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
async start() {
|
|
89
|
+
const proofSubmissionEpochs = await this.rollup.getProofSubmissionEpochs();
|
|
90
|
+
this.proofSubmissionEpochs = Number(proofSubmissionEpochs);
|
|
91
|
+
this.meter.addBatchObservableCallback(this.observe, [
|
|
92
|
+
this.rewards
|
|
93
|
+
]);
|
|
94
|
+
}
|
|
95
|
+
stop() {
|
|
96
|
+
this.meter.removeBatchObservableCallback(this.observe, [
|
|
97
|
+
this.rewards
|
|
98
|
+
]);
|
|
99
|
+
}
|
|
100
|
+
observe;
|
|
101
|
+
}
|
|
102
|
+
export class ProverNodePublisherMetrics {
|
|
103
|
+
client;
|
|
104
|
+
logger;
|
|
105
|
+
gasPrice;
|
|
106
|
+
txCount;
|
|
107
|
+
txDuration;
|
|
108
|
+
txGas;
|
|
109
|
+
txCalldataSize;
|
|
110
|
+
txCalldataGas;
|
|
111
|
+
txBlobDataGasUsed;
|
|
112
|
+
txBlobDataGasCost;
|
|
113
|
+
txTotalFee;
|
|
114
|
+
senderBalance;
|
|
115
|
+
meter;
|
|
116
|
+
constructor(client, name = 'ProverNode', logger = createLogger('prover-node:publisher:metrics')){
|
|
117
|
+
this.client = client;
|
|
118
|
+
this.logger = logger;
|
|
119
|
+
this.meter = client.getMeter(name);
|
|
120
|
+
this.gasPrice = this.meter.createHistogram(Metrics.L1_PUBLISHER_GAS_PRICE, {
|
|
43
121
|
description: 'The gas price used for transactions',
|
|
44
122
|
unit: 'gwei',
|
|
45
123
|
valueType: ValueType.DOUBLE
|
|
46
124
|
});
|
|
47
|
-
this.txCount = meter.createUpDownCounter(Metrics.L1_PUBLISHER_TX_COUNT, {
|
|
125
|
+
this.txCount = this.meter.createUpDownCounter(Metrics.L1_PUBLISHER_TX_COUNT, {
|
|
48
126
|
description: 'The number of transactions processed'
|
|
49
127
|
});
|
|
50
|
-
this.txDuration = meter.createHistogram(Metrics.L1_PUBLISHER_TX_DURATION, {
|
|
128
|
+
this.txDuration = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_DURATION, {
|
|
51
129
|
description: 'The duration of transaction processing',
|
|
52
130
|
unit: 'ms',
|
|
53
131
|
valueType: ValueType.INT
|
|
54
132
|
});
|
|
55
|
-
this.txGas = meter.createHistogram(Metrics.L1_PUBLISHER_TX_GAS, {
|
|
133
|
+
this.txGas = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_GAS, {
|
|
56
134
|
description: 'The gas consumed by transactions',
|
|
57
135
|
unit: 'gas',
|
|
58
136
|
valueType: ValueType.INT
|
|
59
137
|
});
|
|
60
|
-
this.txCalldataSize = meter.createHistogram(Metrics.L1_PUBLISHER_TX_CALLDATA_SIZE, {
|
|
138
|
+
this.txCalldataSize = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_CALLDATA_SIZE, {
|
|
61
139
|
description: 'The size of the calldata in transactions',
|
|
62
140
|
unit: 'By',
|
|
63
141
|
valueType: ValueType.INT
|
|
64
142
|
});
|
|
65
|
-
this.txCalldataGas = meter.createHistogram(Metrics.L1_PUBLISHER_TX_CALLDATA_GAS, {
|
|
143
|
+
this.txCalldataGas = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_CALLDATA_GAS, {
|
|
66
144
|
description: 'The gas consumed by the calldata in transactions',
|
|
67
145
|
unit: 'gas',
|
|
68
146
|
valueType: ValueType.INT
|
|
69
147
|
});
|
|
70
|
-
this.txBlobDataGasUsed = meter.createHistogram(Metrics.L1_PUBLISHER_TX_BLOBDATA_GAS_USED, {
|
|
148
|
+
this.txBlobDataGasUsed = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_BLOBDATA_GAS_USED, {
|
|
71
149
|
description: 'The amount of blob gas used in transactions',
|
|
72
150
|
unit: 'gas',
|
|
73
151
|
valueType: ValueType.INT
|
|
74
152
|
});
|
|
75
|
-
this.txBlobDataGasCost = meter.createHistogram(Metrics.L1_PUBLISHER_TX_BLOBDATA_GAS_COST, {
|
|
153
|
+
this.txBlobDataGasCost = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_BLOBDATA_GAS_COST, {
|
|
76
154
|
description: 'The gas cost of blobs in transactions',
|
|
77
155
|
unit: 'gwei',
|
|
78
156
|
valueType: ValueType.INT
|
|
79
157
|
});
|
|
80
|
-
this.
|
|
158
|
+
this.txTotalFee = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_TOTAL_FEE, {
|
|
159
|
+
description: 'How much L1 tx costs',
|
|
160
|
+
unit: 'gwei',
|
|
161
|
+
valueType: ValueType.DOUBLE,
|
|
162
|
+
advice: {
|
|
163
|
+
explicitBucketBoundaries: [
|
|
164
|
+
0.001,
|
|
165
|
+
0.002,
|
|
166
|
+
0.004,
|
|
167
|
+
0.008,
|
|
168
|
+
0.01,
|
|
169
|
+
0.02,
|
|
170
|
+
0.04,
|
|
171
|
+
0.08,
|
|
172
|
+
0.1,
|
|
173
|
+
0.2,
|
|
174
|
+
0.4,
|
|
175
|
+
0.8,
|
|
176
|
+
1,
|
|
177
|
+
1.2,
|
|
178
|
+
1.4,
|
|
179
|
+
1.8,
|
|
180
|
+
2
|
|
181
|
+
]
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
this.senderBalance = this.meter.createGauge(Metrics.L1_PUBLISHER_BALANCE, {
|
|
81
185
|
unit: 'eth',
|
|
82
186
|
description: 'The balance of the sender address',
|
|
83
187
|
valueType: ValueType.DOUBLE
|
|
@@ -92,12 +196,6 @@ export class ProverNodeMetrics {
|
|
|
92
196
|
recordSubmitProof(durationMs, stats) {
|
|
93
197
|
this.recordTx(durationMs, stats);
|
|
94
198
|
}
|
|
95
|
-
recordProvingJob(executionTimeMs, totalTimeMs, numBlocks, numTxs) {
|
|
96
|
-
this.proverEpochExecutionDuration.record(Math.ceil(executionTimeMs));
|
|
97
|
-
this.provingJobDuration.record(totalTimeMs / 1000);
|
|
98
|
-
this.provingJobBlocks.record(Math.floor(numBlocks));
|
|
99
|
-
this.provingJobTransactions.record(Math.floor(numTxs));
|
|
100
|
-
}
|
|
101
199
|
recordSenderBalance(wei, senderAddress) {
|
|
102
200
|
const eth = parseFloat(formatEther(wei, 'wei'));
|
|
103
201
|
this.senderBalance.record(eth, {
|
|
@@ -122,7 +220,15 @@ export class ProverNodeMetrics {
|
|
|
122
220
|
this.txBlobDataGasUsed.record(Number(stats.blobGasUsed), attributes);
|
|
123
221
|
try {
|
|
124
222
|
this.gasPrice.record(parseInt(formatEther(stats.gasPrice, 'gwei'), 10));
|
|
125
|
-
} catch
|
|
223
|
+
} catch {
|
|
224
|
+
// ignore
|
|
225
|
+
}
|
|
226
|
+
const executionFee = stats.gasUsed * stats.gasPrice;
|
|
227
|
+
const blobFee = stats.blobGasUsed * stats.blobDataGas;
|
|
228
|
+
const totalFee = executionFee + blobFee;
|
|
229
|
+
try {
|
|
230
|
+
this.txTotalFee.record(parseFloat(formatEther(totalFee)));
|
|
231
|
+
} catch {
|
|
126
232
|
// ignore
|
|
127
233
|
}
|
|
128
234
|
}
|
|
@@ -2,7 +2,7 @@ import type { L2BlockSource } from '@aztec/stdlib/block';
|
|
|
2
2
|
import { type L1RollupConstants } from '@aztec/stdlib/epoch-helpers';
|
|
3
3
|
import { type TelemetryClient, type Traceable, type Tracer } from '@aztec/telemetry-client';
|
|
4
4
|
export interface EpochMonitorHandler {
|
|
5
|
-
handleEpochReadyToProve(epochNumber: bigint): Promise<
|
|
5
|
+
handleEpochReadyToProve(epochNumber: bigint): Promise<boolean>;
|
|
6
6
|
}
|
|
7
7
|
/**
|
|
8
8
|
* Fires an event when a new epoch ready to prove is detected.
|
|
@@ -26,9 +26,11 @@ export declare class EpochMonitor implements Traceable {
|
|
|
26
26
|
private latestEpochNumber;
|
|
27
27
|
constructor(l2BlockSource: L2BlockSource, l1Constants: Pick<L1RollupConstants, 'epochDuration'>, options: {
|
|
28
28
|
pollingIntervalMs: number;
|
|
29
|
+
provingDelayMs?: number;
|
|
29
30
|
}, telemetry?: TelemetryClient);
|
|
30
31
|
static create(l2BlockSource: L2BlockSource, options: {
|
|
31
32
|
pollingIntervalMs: number;
|
|
33
|
+
provingDelayMs?: number;
|
|
32
34
|
}, telemetry?: TelemetryClient): Promise<EpochMonitor>;
|
|
33
35
|
start(handler: EpochMonitorHandler): void;
|
|
34
36
|
/** Exposed for testing */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"epoch-monitor.d.ts","sourceRoot":"","sources":["../../src/monitors/epoch-monitor.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"epoch-monitor.d.ts","sourceRoot":"","sources":["../../src/monitors/epoch-monitor.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,KAAK,iBAAiB,EAAkB,MAAM,6BAA6B,CAAC;AACrF,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,SAAS,EACd,KAAK,MAAM,EAGZ,MAAM,yBAAyB,CAAC;AAEjC,MAAM,WAAW,mBAAmB;IAClC,uBAAuB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAChE;AAED;;;;;;;;;;GAUG;AACH,qBAAa,YAAa,YAAW,SAAS;IAS1C,OAAO,CAAC,QAAQ,CAAC,aAAa;IAC9B,OAAO,CAAC,QAAQ,CAAC,WAAW;IAC5B,OAAO,CAAC,OAAO;IAVjB,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,GAAG,CAA6C;IACxD,SAAgB,MAAM,EAAE,MAAM,CAAC;IAE/B,OAAO,CAAC,OAAO,CAAkC;IACjD,OAAO,CAAC,iBAAiB,CAAqB;gBAG3B,aAAa,EAAE,aAAa,EAC5B,WAAW,EAAE,IAAI,CAAC,iBAAiB,EAAE,eAAe,CAAC,EAC9D,OAAO,EAAE;QAAE,iBAAiB,EAAE,MAAM,CAAC;QAAC,cAAc,CAAC,EAAE,MAAM,CAAA;KAAE,EACvE,SAAS,GAAE,eAAsC;WAS/B,MAAM,CACxB,aAAa,EAAE,aAAa,EAC5B,OAAO,EAAE;QAAE,iBAAiB,EAAE,MAAM,CAAC;QAAC,cAAc,CAAC,EAAE,MAAM,CAAA;KAAE,EAC/D,SAAS,GAAE,eAAsC,GAChD,OAAO,CAAC,YAAY,CAAC;IAKjB,KAAK,CAAC,OAAO,EAAE,mBAAmB;IAMzC,0BAA0B;IACnB,UAAU,CAAC,OAAO,EAAE,mBAAmB;IAIjC,IAAI;IAMJ,IAAI;YA6BH,qBAAqB;CAYpC"}
|