@aztec/prover-node 0.0.1-commit.0208eb9 → 0.0.1-commit.031e54b
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.js +1 -1
- package/dest/actions/rerun-epoch-proving-job.d.ts +4 -3
- package/dest/actions/rerun-epoch-proving-job.d.ts.map +1 -1
- package/dest/actions/rerun-epoch-proving-job.js +8 -8
- package/dest/actions/upload-epoch-proof-failure.d.ts +2 -2
- package/dest/actions/upload-epoch-proof-failure.d.ts.map +1 -1
- package/dest/bin/run-failed-epoch.js +6 -5
- package/dest/config.d.ts +5 -8
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +16 -19
- package/dest/factory.d.ts +19 -13
- package/dest/factory.d.ts.map +1 -1
- package/dest/factory.js +40 -60
- package/dest/job/epoch-proving-job.d.ts +6 -2
- package/dest/job/epoch-proving-job.d.ts.map +1 -1
- package/dest/job/epoch-proving-job.js +206 -35
- package/dest/metrics.d.ts +21 -1
- package/dest/metrics.d.ts.map +1 -1
- package/dest/metrics.js +47 -0
- package/dest/monitors/epoch-monitor.d.ts +1 -1
- package/dest/monitors/epoch-monitor.d.ts.map +1 -1
- package/dest/monitors/epoch-monitor.js +11 -9
- package/dest/prover-node-publisher.d.ts +22 -3
- package/dest/prover-node-publisher.d.ts.map +1 -1
- package/dest/prover-node-publisher.js +202 -7
- package/dest/prover-node.d.ts +24 -14
- package/dest/prover-node.d.ts.map +1 -1
- package/dest/prover-node.js +91 -41
- package/dest/prover-publisher-factory.d.ts +4 -4
- package/dest/prover-publisher-factory.d.ts.map +1 -1
- package/dest/prover-publisher-factory.js +3 -3
- package/package.json +23 -22
- package/src/actions/download-epoch-proving-job.ts +1 -1
- package/src/actions/rerun-epoch-proving-job.ts +17 -6
- package/src/actions/upload-epoch-proof-failure.ts +1 -1
- package/src/bin/run-failed-epoch.ts +5 -3
- package/src/config.ts +23 -31
- package/src/factory.ts +68 -103
- package/src/job/epoch-proving-job.ts +137 -41
- package/src/metrics.ts +71 -0
- package/src/monitors/epoch-monitor.ts +5 -6
- package/src/prover-node-publisher.ts +235 -12
- package/src/prover-node.ts +98 -55
- package/src/prover-publisher-factory.ts +5 -5
package/src/factory.ts
CHANGED
|
@@ -1,34 +1,36 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import type { Archiver } from '@aztec/archiver';
|
|
2
|
+
import type { BlobClientInterface } from '@aztec/blob-client/client';
|
|
3
|
+
import { Blob } from '@aztec/blob-lib';
|
|
4
|
+
import type { EpochCacheInterface } from '@aztec/epoch-cache';
|
|
5
5
|
import { createEthereumChain } from '@aztec/ethereum/chain';
|
|
6
|
+
import { makeL1HttpTransport } from '@aztec/ethereum/client';
|
|
6
7
|
import { RollupContract } from '@aztec/ethereum/contracts';
|
|
7
8
|
import { L1TxUtils } from '@aztec/ethereum/l1-tx-utils';
|
|
8
9
|
import { PublisherManager } from '@aztec/ethereum/publisher-manager';
|
|
9
10
|
import { pick } from '@aztec/foundation/collection';
|
|
10
11
|
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
11
12
|
import { DateProvider } from '@aztec/foundation/timer';
|
|
12
|
-
import
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
createForwarderL1TxUtilsFromEthSigner,
|
|
17
|
-
createL1TxUtilsFromEthSignerWithStore,
|
|
18
|
-
} from '@aztec/node-lib/factories';
|
|
19
|
-
import { NodeRpcTxSource, createP2PClient } from '@aztec/p2p';
|
|
20
|
-
import { type ProverClientConfig, createProverClient } from '@aztec/prover-client';
|
|
13
|
+
import { KeystoreManager } from '@aztec/node-keystore';
|
|
14
|
+
import { createForwarderL1TxUtilsFromSigners, createL1TxUtilsFromSigners } from '@aztec/node-lib/factories';
|
|
15
|
+
import { type ProverClientConfig, type ProverClientUserConfig, createProverClient } from '@aztec/prover-client';
|
|
21
16
|
import { createAndStartProvingBroker } from '@aztec/prover-client/broker';
|
|
22
|
-
import
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
17
|
+
import {
|
|
18
|
+
type ProverPublisherConfig,
|
|
19
|
+
type ProverTxSenderConfig,
|
|
20
|
+
getPublisherConfigFromProverConfig,
|
|
21
|
+
} from '@aztec/sequencer-client';
|
|
22
|
+
import type {
|
|
23
|
+
ITxProvider,
|
|
24
|
+
ProverConfig,
|
|
25
|
+
ProvingJobBroker,
|
|
26
|
+
Service,
|
|
27
|
+
WorldStateSynchronizer,
|
|
28
|
+
} from '@aztec/stdlib/interfaces/server';
|
|
26
29
|
import { L1Metrics, type TelemetryClient, getTelemetryClient } from '@aztec/telemetry-client';
|
|
27
|
-
import { createWorldStateSynchronizer } from '@aztec/world-state';
|
|
28
30
|
|
|
29
|
-
import { createPublicClient
|
|
31
|
+
import { createPublicClient } from 'viem';
|
|
30
32
|
|
|
31
|
-
import
|
|
33
|
+
import type { SpecificProverNodeConfig } from './config.js';
|
|
32
34
|
import { EpochMonitor } from './monitors/epoch-monitor.js';
|
|
33
35
|
import { ProverNode } from './prover-node.js';
|
|
34
36
|
import { ProverPublisherFactory } from './prover-publisher-factory.js';
|
|
@@ -36,54 +38,42 @@ import { ProverPublisherFactory } from './prover-publisher-factory.js';
|
|
|
36
38
|
export type ProverNodeDeps = {
|
|
37
39
|
telemetry?: TelemetryClient;
|
|
38
40
|
log?: Logger;
|
|
39
|
-
|
|
40
|
-
archiver?: Archiver;
|
|
41
|
+
archiver: Archiver;
|
|
41
42
|
publisherFactory?: ProverPublisherFactory;
|
|
42
43
|
broker?: ProvingJobBroker;
|
|
43
44
|
l1TxUtils?: L1TxUtils;
|
|
44
45
|
dateProvider?: DateProvider;
|
|
46
|
+
worldStateSynchronizer: WorldStateSynchronizer;
|
|
47
|
+
p2pClient: { getTxProvider(): ITxProvider } & Partial<Service>;
|
|
48
|
+
epochCache: EpochCacheInterface;
|
|
49
|
+
blobClient: BlobClientInterface;
|
|
50
|
+
keyStoreManager?: KeystoreManager;
|
|
45
51
|
};
|
|
46
52
|
|
|
47
|
-
/** Creates a new prover node given a config
|
|
53
|
+
/** Creates a new prover node subsystem given a config and dependencies */
|
|
48
54
|
export async function createProverNode(
|
|
49
|
-
userConfig:
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
55
|
+
userConfig: SpecificProverNodeConfig &
|
|
56
|
+
ProverConfig &
|
|
57
|
+
ProverClientUserConfig &
|
|
58
|
+
ProverPublisherConfig &
|
|
59
|
+
ProverTxSenderConfig,
|
|
60
|
+
deps: ProverNodeDeps,
|
|
54
61
|
) {
|
|
55
62
|
const config = { ...userConfig };
|
|
56
63
|
const telemetry = deps.telemetry ?? getTelemetryClient();
|
|
57
64
|
const dateProvider = deps.dateProvider ?? new DateProvider();
|
|
58
|
-
const
|
|
59
|
-
const log = deps.log ?? createLogger('prover-node');
|
|
60
|
-
|
|
61
|
-
// Build a key store from file if given or from environment otherwise
|
|
62
|
-
let keyStoreManager: KeystoreManager | undefined;
|
|
63
|
-
const keyStoreProvided = config.keyStoreDirectory !== undefined && config.keyStoreDirectory.length > 0;
|
|
64
|
-
if (keyStoreProvided) {
|
|
65
|
-
const keyStores = loadKeystores(config.keyStoreDirectory!);
|
|
66
|
-
keyStoreManager = new KeystoreManager(mergeKeystores(keyStores));
|
|
67
|
-
} else {
|
|
68
|
-
const keyStore = createKeyStoreForProver(config);
|
|
69
|
-
if (keyStore) {
|
|
70
|
-
keyStoreManager = new KeystoreManager(keyStore);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
65
|
+
const log = deps.log ?? createLogger('prover');
|
|
73
66
|
|
|
74
|
-
|
|
67
|
+
const { p2pClient, archiver, keyStoreManager, worldStateSynchronizer } = deps;
|
|
75
68
|
|
|
76
69
|
// Extract the prover signers from the key store and verify that we have one.
|
|
70
|
+
await keyStoreManager?.validateSigners();
|
|
77
71
|
const proverSigners = keyStoreManager?.createProverSigners();
|
|
78
72
|
|
|
79
73
|
if (proverSigners === undefined) {
|
|
80
74
|
throw new Error('Failed to create prover key store configuration');
|
|
81
75
|
} else if (proverSigners.signers.length === 0) {
|
|
82
76
|
throw new Error('No prover signers found in the key store');
|
|
83
|
-
} else if (!keyStoreProvided) {
|
|
84
|
-
log.warn(
|
|
85
|
-
'KEY STORE CREATED FROM ENVIRONMENT, IT IS RECOMMENDED TO USE A FILE-BASED KEY STORE IN PRODUCTION ENVIRONMENTS',
|
|
86
|
-
);
|
|
87
77
|
}
|
|
88
78
|
|
|
89
79
|
log.info(`Creating prover with publishers ${proverSigners.signers.map(signer => signer.address.toString()).join()}`);
|
|
@@ -95,27 +85,7 @@ export async function createProverNode(
|
|
|
95
85
|
const proverId = proverSigners.id ?? proverIdInUserConfig ?? proverSigners.signers[0].address;
|
|
96
86
|
|
|
97
87
|
// Now create the prover client configuration from this.
|
|
98
|
-
const proverClientConfig: ProverClientConfig = {
|
|
99
|
-
...config,
|
|
100
|
-
proverId,
|
|
101
|
-
};
|
|
102
|
-
|
|
103
|
-
await trySnapshotSync(config, log);
|
|
104
|
-
|
|
105
|
-
const epochCache = await EpochCache.create(config.l1Contracts.rollupAddress, config);
|
|
106
|
-
|
|
107
|
-
const archiver =
|
|
108
|
-
deps.archiver ??
|
|
109
|
-
(await createArchiver(config, { blobClient, epochCache, telemetry, dateProvider }, { blockUntilSync: true }));
|
|
110
|
-
log.verbose(`Created archiver and synced to block ${await archiver.getBlockNumber()}`);
|
|
111
|
-
|
|
112
|
-
const worldStateSynchronizer = await createWorldStateSynchronizer(
|
|
113
|
-
config,
|
|
114
|
-
archiver,
|
|
115
|
-
options.prefilledPublicData,
|
|
116
|
-
telemetry,
|
|
117
|
-
);
|
|
118
|
-
await worldStateSynchronizer.start();
|
|
88
|
+
const proverClientConfig: ProverClientConfig = { ...config, proverId };
|
|
119
89
|
|
|
120
90
|
const broker = deps.broker ?? (await createAndStartProvingBroker(config, telemetry));
|
|
121
91
|
|
|
@@ -126,63 +96,53 @@ export async function createProverNode(
|
|
|
126
96
|
|
|
127
97
|
const publicClient = createPublicClient({
|
|
128
98
|
chain: chain.chainInfo,
|
|
129
|
-
transport:
|
|
99
|
+
transport: makeL1HttpTransport(config.l1RpcUrls, { timeout: config.l1HttpTimeoutMS }),
|
|
130
100
|
pollingInterval: config.viemPollingIntervalMS,
|
|
131
101
|
});
|
|
132
102
|
|
|
133
|
-
const rollupContract = new RollupContract(publicClient, config.
|
|
103
|
+
const rollupContract = new RollupContract(publicClient, config.rollupAddress.toString());
|
|
134
104
|
|
|
135
105
|
const l1TxUtils = deps.l1TxUtils
|
|
136
106
|
? [deps.l1TxUtils]
|
|
137
|
-
: config.
|
|
138
|
-
? await
|
|
107
|
+
: config.proverPublisherForwarderAddress
|
|
108
|
+
? await createForwarderL1TxUtilsFromSigners(
|
|
139
109
|
publicClient,
|
|
140
110
|
proverSigners.signers,
|
|
141
|
-
config.
|
|
111
|
+
config.proverPublisherForwarderAddress,
|
|
142
112
|
{ ...config, scope: 'prover' },
|
|
143
|
-
{ telemetry, logger: log.createChild('l1-tx-utils'), dateProvider },
|
|
113
|
+
{ telemetry, logger: log.createChild('l1-tx-utils'), dateProvider, kzg: Blob.getViemKzgInstance() },
|
|
144
114
|
)
|
|
145
|
-
: await
|
|
115
|
+
: await createL1TxUtilsFromSigners(
|
|
146
116
|
publicClient,
|
|
147
117
|
proverSigners.signers,
|
|
148
118
|
{ ...config, scope: 'prover' },
|
|
149
119
|
{ telemetry, logger: log.createChild('l1-tx-utils'), dateProvider },
|
|
150
120
|
);
|
|
151
121
|
|
|
122
|
+
// Create a funder L1TxUtils from the keystore funding account (if configured)
|
|
123
|
+
const fundingSigner = keyStoreManager?.createFundingSigner();
|
|
124
|
+
let funderL1TxUtils: L1TxUtils | undefined;
|
|
125
|
+
if (fundingSigner) {
|
|
126
|
+
const [funder] = await createL1TxUtilsFromSigners(
|
|
127
|
+
publicClient,
|
|
128
|
+
[fundingSigner],
|
|
129
|
+
{ ...config, scope: 'prover' },
|
|
130
|
+
{ telemetry, logger: log.createChild('l1-tx-utils:funder'), dateProvider },
|
|
131
|
+
);
|
|
132
|
+
funderL1TxUtils = funder;
|
|
133
|
+
}
|
|
134
|
+
|
|
152
135
|
const publisherFactory =
|
|
153
136
|
deps.publisherFactory ??
|
|
154
137
|
new ProverPublisherFactory(config, {
|
|
155
138
|
rollupContract,
|
|
156
|
-
publisherManager: new PublisherManager(l1TxUtils, config,
|
|
139
|
+
publisherManager: new PublisherManager(l1TxUtils, getPublisherConfigFromProverConfig(config), {
|
|
140
|
+
bindings: log.getBindings(),
|
|
141
|
+
funder: funderL1TxUtils,
|
|
142
|
+
}),
|
|
157
143
|
telemetry,
|
|
158
144
|
});
|
|
159
145
|
|
|
160
|
-
const proofVerifier = new QueuedIVCVerifier(
|
|
161
|
-
config,
|
|
162
|
-
config.realProofs || config.debugForceTxProofVerification
|
|
163
|
-
? await BBCircuitVerifier.new(config)
|
|
164
|
-
: new TestCircuitVerifier(config.proverTestVerificationDelayMs),
|
|
165
|
-
);
|
|
166
|
-
|
|
167
|
-
const p2pClient = await createP2PClient(
|
|
168
|
-
P2PClientType.Prover,
|
|
169
|
-
config,
|
|
170
|
-
archiver,
|
|
171
|
-
proofVerifier,
|
|
172
|
-
worldStateSynchronizer,
|
|
173
|
-
epochCache,
|
|
174
|
-
getPackageVersion() ?? '',
|
|
175
|
-
dateProvider,
|
|
176
|
-
telemetry,
|
|
177
|
-
{
|
|
178
|
-
txCollectionNodeSources: deps.aztecNodeTxProvider
|
|
179
|
-
? [new NodeRpcTxSource(deps.aztecNodeTxProvider, 'TestNode')]
|
|
180
|
-
: [],
|
|
181
|
-
},
|
|
182
|
-
);
|
|
183
|
-
|
|
184
|
-
await p2pClient.start();
|
|
185
|
-
|
|
186
146
|
const proverNodeConfig = {
|
|
187
147
|
...pick(
|
|
188
148
|
config,
|
|
@@ -213,6 +173,9 @@ export async function createProverNode(
|
|
|
213
173
|
l1TxUtils.map(utils => utils.getSenderAddress()),
|
|
214
174
|
);
|
|
215
175
|
|
|
176
|
+
// Extract the shared delayer from the first L1TxUtils instance (all instances share the same delayer)
|
|
177
|
+
const delayer = l1TxUtils[0]?.delayer;
|
|
178
|
+
|
|
216
179
|
return new ProverNode(
|
|
217
180
|
prover,
|
|
218
181
|
publisherFactory,
|
|
@@ -226,5 +189,7 @@ export async function createProverNode(
|
|
|
226
189
|
l1Metrics,
|
|
227
190
|
proverNodeConfig,
|
|
228
191
|
telemetry,
|
|
192
|
+
delayer,
|
|
193
|
+
dateProvider,
|
|
229
194
|
);
|
|
230
195
|
}
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import { NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP } from '@aztec/constants';
|
|
2
1
|
import { asyncPool } from '@aztec/foundation/async-pool';
|
|
3
2
|
import { BlockNumber, EpochNumber } from '@aztec/foundation/branded-types';
|
|
4
|
-
import { padArrayEnd } from '@aztec/foundation/collection';
|
|
5
3
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
6
4
|
import { type Logger, type LoggerBindings, createLogger } from '@aztec/foundation/log';
|
|
7
5
|
import { RunningPromise, promiseWithResolvers } from '@aztec/foundation/promise';
|
|
8
6
|
import { Timer } from '@aztec/foundation/timer';
|
|
7
|
+
import { AVM_MAX_CONCURRENT_SIMULATIONS } from '@aztec/native';
|
|
9
8
|
import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types/vk-tree';
|
|
10
9
|
import { protocolContractsHash } from '@aztec/protocol-contracts';
|
|
11
10
|
import { buildFinalBlobChallenges } from '@aztec/prover-client/helpers';
|
|
@@ -19,8 +18,8 @@ import {
|
|
|
19
18
|
EpochProvingJobTerminalState,
|
|
20
19
|
type ForkMerkleTreeOperations,
|
|
21
20
|
} from '@aztec/stdlib/interfaces/server';
|
|
21
|
+
import { appendL1ToL2MessagesToTree } from '@aztec/stdlib/messaging';
|
|
22
22
|
import { CheckpointConstantData } from '@aztec/stdlib/rollup';
|
|
23
|
-
import { MerkleTreeId } from '@aztec/stdlib/trees';
|
|
24
23
|
import type { ProcessedTx, Tx } from '@aztec/stdlib/tx';
|
|
25
24
|
import { Attributes, type Traceable, type Tracer, trackSpan } from '@aztec/telemetry-client';
|
|
26
25
|
|
|
@@ -47,6 +46,7 @@ export class EpochProvingJob implements Traceable {
|
|
|
47
46
|
private uuid: string;
|
|
48
47
|
|
|
49
48
|
private runPromise: Promise<void> | undefined;
|
|
49
|
+
private abortController = new AbortController();
|
|
50
50
|
private epochCheckPromise: RunningPromise | undefined;
|
|
51
51
|
private deadlineTimeoutHandler: NodeJS.Timeout | undefined;
|
|
52
52
|
|
|
@@ -57,7 +57,7 @@ export class EpochProvingJob implements Traceable {
|
|
|
57
57
|
private dbProvider: Pick<ForkMerkleTreeOperations, 'fork'>,
|
|
58
58
|
private prover: EpochProver,
|
|
59
59
|
private publicProcessorFactory: PublicProcessorFactory,
|
|
60
|
-
private publisher: Pick<ProverNodePublisher, 'submitEpochProof'>,
|
|
60
|
+
private publisher: Pick<ProverNodePublisher, 'submitEpochProof' | 'analyzeEpochProofSubmission'>,
|
|
61
61
|
private l2BlockSource: L2BlockSource | undefined,
|
|
62
62
|
private metrics: ProverNodeJobMetrics,
|
|
63
63
|
private deadline: Date | undefined,
|
|
@@ -148,19 +148,32 @@ export class EpochProvingJob implements Traceable {
|
|
|
148
148
|
this.runPromise = promise;
|
|
149
149
|
|
|
150
150
|
try {
|
|
151
|
+
const blobTimer = new Timer();
|
|
151
152
|
const blobFieldsPerCheckpoint = this.checkpoints.map(checkpoint => checkpoint.toBlobFields());
|
|
152
153
|
const finalBlobBatchingChallenges = await buildFinalBlobChallenges(blobFieldsPerCheckpoint);
|
|
154
|
+
this.metrics.recordBlobProcessing(blobTimer.ms());
|
|
153
155
|
|
|
154
156
|
this.prover.startNewEpoch(epochNumber, epochSizeCheckpoints, finalBlobBatchingChallenges);
|
|
157
|
+
const chonkTimer = new Timer();
|
|
155
158
|
await this.prover.startChonkVerifierCircuits(Array.from(this.txs.values()));
|
|
159
|
+
this.metrics.recordChonkVerifier(chonkTimer.ms());
|
|
156
160
|
|
|
157
161
|
// Everything in the epoch should have the same chainId and version.
|
|
158
162
|
const { chainId, version } = this.checkpoints[0].blocks[0].header.globalVariables;
|
|
159
163
|
|
|
160
164
|
const previousBlockHeaders = this.gatherPreviousBlockHeaders();
|
|
161
165
|
|
|
162
|
-
|
|
166
|
+
const allCheckpointsTimer = new Timer();
|
|
167
|
+
|
|
168
|
+
const parallelism = this.config.parallelBlockLimit
|
|
169
|
+
? this.config.parallelBlockLimit
|
|
170
|
+
: AVM_MAX_CONCURRENT_SIMULATIONS > 0
|
|
171
|
+
? AVM_MAX_CONCURRENT_SIMULATIONS
|
|
172
|
+
: this.checkpoints.length;
|
|
173
|
+
|
|
174
|
+
await this.processCheckpoints(parallelism, async checkpoint => {
|
|
163
175
|
this.checkState();
|
|
176
|
+
const checkpointTimer = new Timer();
|
|
164
177
|
|
|
165
178
|
const checkpointIndex = checkpoint.number - fromCheckpoint;
|
|
166
179
|
const checkpointConstants = CheckpointConstantData.from({
|
|
@@ -177,11 +190,12 @@ export class EpochProvingJob implements Traceable {
|
|
|
177
190
|
const previousHeader = previousBlockHeaders[checkpointIndex];
|
|
178
191
|
const l1ToL2Messages = this.getL1ToL2Messages(checkpoint);
|
|
179
192
|
|
|
180
|
-
this.log.
|
|
193
|
+
this.log.debug(`Starting processing checkpoint ${checkpoint.number}`, {
|
|
181
194
|
number: checkpoint.number,
|
|
182
195
|
checkpointHash: checkpoint.hash().toString(),
|
|
183
|
-
|
|
184
|
-
|
|
196
|
+
headerHash: checkpoint.header.hash().toString(),
|
|
197
|
+
numL1ToL2Messages: l1ToL2Messages.length,
|
|
198
|
+
previousBlockNumber: previousHeader.globalVariables.blockNumber,
|
|
185
199
|
uuid: this.uuid,
|
|
186
200
|
});
|
|
187
201
|
|
|
@@ -193,7 +207,9 @@ export class EpochProvingJob implements Traceable {
|
|
|
193
207
|
previousHeader,
|
|
194
208
|
);
|
|
195
209
|
|
|
196
|
-
for (
|
|
210
|
+
for (let blockIndex = 0; blockIndex < checkpoint.blocks.length; blockIndex++) {
|
|
211
|
+
const blockTimer = new Timer();
|
|
212
|
+
const block = checkpoint.blocks[blockIndex];
|
|
197
213
|
const globalVariables = block.header.globalVariables;
|
|
198
214
|
const txs = this.getTxs(block);
|
|
199
215
|
|
|
@@ -211,20 +227,28 @@ export class EpochProvingJob implements Traceable {
|
|
|
211
227
|
// Start block proving
|
|
212
228
|
await this.prover.startNewBlock(block.number, globalVariables.timestamp, txs.length);
|
|
213
229
|
|
|
214
|
-
// Process public fns
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
230
|
+
// Process public fns. L1 to L2 messages are only inserted for the first block of a checkpoint,
|
|
231
|
+
// as the fork for subsequent blocks already includes them from the previous block's synced state.
|
|
232
|
+
{
|
|
233
|
+
await using db = await this.createFork(
|
|
234
|
+
BlockNumber(block.number - 1),
|
|
235
|
+
blockIndex === 0 ? l1ToL2Messages : undefined,
|
|
236
|
+
);
|
|
237
|
+
this.checkState();
|
|
238
|
+
const config = PublicSimulatorConfig.from({
|
|
239
|
+
proverId: this.prover.getProverId().toField(),
|
|
240
|
+
skipFeeEnforcement: false,
|
|
241
|
+
collectDebugLogs: false,
|
|
242
|
+
collectHints: true,
|
|
243
|
+
collectPublicInputs: true,
|
|
244
|
+
collectStatistics: false,
|
|
245
|
+
});
|
|
246
|
+
const publicProcessor = this.publicProcessorFactory.create(db, globalVariables, config);
|
|
247
|
+
const processed = await this.processTxs(publicProcessor, txs);
|
|
248
|
+
this.checkState();
|
|
249
|
+
await this.prover.addTxs(processed);
|
|
250
|
+
}
|
|
251
|
+
this.checkState();
|
|
228
252
|
this.log.verbose(`Processed all ${txs.length} txs for block ${block.number}`, {
|
|
229
253
|
blockNumber: block.number,
|
|
230
254
|
blockHash: (await block.hash()).toString(),
|
|
@@ -234,8 +258,11 @@ export class EpochProvingJob implements Traceable {
|
|
|
234
258
|
// Mark block as completed to pad it
|
|
235
259
|
const expectedBlockHeader = block.header;
|
|
236
260
|
await this.prover.setBlockCompleted(block.number, expectedBlockHeader);
|
|
261
|
+
this.metrics.recordBlockProcessing(blockTimer.ms());
|
|
237
262
|
}
|
|
263
|
+
this.metrics.recordCheckpointProcessing(checkpointTimer.ms());
|
|
238
264
|
});
|
|
265
|
+
this.metrics.recordAllCheckpointsProcessing(allCheckpointsTimer.ms());
|
|
239
266
|
|
|
240
267
|
const executionTime = timer.ms();
|
|
241
268
|
|
|
@@ -247,8 +274,21 @@ export class EpochProvingJob implements Traceable {
|
|
|
247
274
|
|
|
248
275
|
if (this.config.skipSubmitProof) {
|
|
249
276
|
this.log.info(
|
|
250
|
-
`Proof publishing is disabled.
|
|
277
|
+
`Proof publishing is disabled. Analyzing estimated L1 fees for epoch ${epochNumber} (checkpoints ${fromCheckpoint} to ${toCheckpoint})`,
|
|
251
278
|
);
|
|
279
|
+
try {
|
|
280
|
+
await this.publisher.analyzeEpochProofSubmission({
|
|
281
|
+
fromCheckpoint,
|
|
282
|
+
toCheckpoint,
|
|
283
|
+
epochNumber,
|
|
284
|
+
publicInputs,
|
|
285
|
+
proof,
|
|
286
|
+
batchedBlobInputs,
|
|
287
|
+
attestations,
|
|
288
|
+
});
|
|
289
|
+
} catch (err) {
|
|
290
|
+
this.log.warn(`Failed to analyze estimated L1 fees for epoch ${epochNumber}`, err);
|
|
291
|
+
}
|
|
252
292
|
this.state = 'completed';
|
|
253
293
|
this.metrics.recordProvingJob(executionTime, timer.ms(), epochSizeCheckpoints, epochSizeBlocks, epochSizeTxs);
|
|
254
294
|
return;
|
|
@@ -295,25 +335,63 @@ export class EpochProvingJob implements Traceable {
|
|
|
295
335
|
}
|
|
296
336
|
|
|
297
337
|
/**
|
|
298
|
-
* Create a new db fork for tx processing, inserting
|
|
338
|
+
* Create a new db fork for tx processing, optionally inserting L1 to L2 messages.
|
|
339
|
+
* L1 to L2 messages should only be inserted for the first block in a checkpoint,
|
|
340
|
+
* as subsequent blocks' synced state already includes them.
|
|
299
341
|
* 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.
|
|
300
342
|
*/
|
|
301
|
-
private async createFork(blockNumber: BlockNumber, l1ToL2Messages: Fr[]) {
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
343
|
+
private async createFork(blockNumber: BlockNumber, l1ToL2Messages: Fr[] | undefined) {
|
|
344
|
+
this.log.verbose(`Creating fork at ${blockNumber}`, { blockNumber });
|
|
345
|
+
// temporary stack to control fork lifetime
|
|
346
|
+
await using cleanup = new AsyncDisposableStack();
|
|
347
|
+
const db = cleanup.use(await this.dbProvider.fork(blockNumber));
|
|
348
|
+
|
|
349
|
+
if (l1ToL2Messages !== undefined) {
|
|
350
|
+
this.log.verbose(`Inserting ${l1ToL2Messages.length} L1 to L2 messages in fork`, {
|
|
351
|
+
blockNumber,
|
|
352
|
+
l1ToL2Messages: l1ToL2Messages.map(m => m.toString()),
|
|
353
|
+
});
|
|
354
|
+
await appendL1ToL2MessagesToTree(db, l1ToL2Messages);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// everything run succesfully so we can release this stack and give control of the fork's lifetime to the caller
|
|
358
|
+
cleanup.move();
|
|
314
359
|
return db;
|
|
315
360
|
}
|
|
316
361
|
|
|
362
|
+
private async processCheckpoints(
|
|
363
|
+
parallelism: number,
|
|
364
|
+
processCheckpoint: (checkpoint: Checkpoint) => Promise<void>,
|
|
365
|
+
): Promise<void> {
|
|
366
|
+
let hasError = false;
|
|
367
|
+
let firstError: unknown;
|
|
368
|
+
|
|
369
|
+
await asyncPool(Math.max(parallelism, 1), this.checkpoints, async checkpoint => {
|
|
370
|
+
if (hasError || this.abortController.signal.aborted) {
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
try {
|
|
375
|
+
this.checkState();
|
|
376
|
+
await processCheckpoint(checkpoint);
|
|
377
|
+
} catch (err) {
|
|
378
|
+
if (!hasError) {
|
|
379
|
+
hasError = true;
|
|
380
|
+
firstError = err;
|
|
381
|
+
this.failProcessing();
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
if (hasError) {
|
|
387
|
+
throw firstError;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
if (this.abortController.signal.aborted) {
|
|
391
|
+
this.checkState();
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
317
395
|
private progressState(state: EpochProvingJobState) {
|
|
318
396
|
this.checkState();
|
|
319
397
|
this.state = state;
|
|
@@ -327,12 +405,24 @@ export class EpochProvingJob implements Traceable {
|
|
|
327
405
|
|
|
328
406
|
public async stop(state: EpochProvingJobTerminalState = 'stopped') {
|
|
329
407
|
this.state = state;
|
|
330
|
-
this.
|
|
408
|
+
this.interruptProcessing();
|
|
331
409
|
if (this.runPromise) {
|
|
332
410
|
await this.runPromise;
|
|
333
411
|
}
|
|
334
412
|
}
|
|
335
413
|
|
|
414
|
+
private failProcessing() {
|
|
415
|
+
if (!EpochProvingJobTerminalState.includes(this.state)) {
|
|
416
|
+
this.state = 'failed';
|
|
417
|
+
}
|
|
418
|
+
this.interruptProcessing();
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
private interruptProcessing() {
|
|
422
|
+
this.abortController.abort();
|
|
423
|
+
this.prover.cancel();
|
|
424
|
+
}
|
|
425
|
+
|
|
336
426
|
private scheduleDeadlineStop() {
|
|
337
427
|
const deadline = this.deadline;
|
|
338
428
|
if (deadline) {
|
|
@@ -367,7 +457,9 @@ export class EpochProvingJob implements Traceable {
|
|
|
367
457
|
const intervalMs = Math.ceil((await l2BlockSource.getL1Constants()).ethereumSlotDuration / 2) * 1000;
|
|
368
458
|
this.epochCheckPromise = new RunningPromise(
|
|
369
459
|
async () => {
|
|
370
|
-
const blockHeaders =
|
|
460
|
+
const blockHeaders = (
|
|
461
|
+
await l2BlockSource.getBlocksData({ epoch: this.epochNumber, onlyCheckpointed: true })
|
|
462
|
+
).map(d => d.header);
|
|
371
463
|
const blockHashes = await Promise.all(blockHeaders.map(header => header.hash()));
|
|
372
464
|
const thisBlocks = this.checkpoints.flatMap(checkpoint => checkpoint.blocks);
|
|
373
465
|
const thisBlockHashes = await Promise.all(thisBlocks.map(block => block.hash()));
|
|
@@ -406,7 +498,11 @@ export class EpochProvingJob implements Traceable {
|
|
|
406
498
|
|
|
407
499
|
private async processTxs(publicProcessor: PublicProcessor, txs: Tx[]): Promise<ProcessedTx[]> {
|
|
408
500
|
const { deadline } = this;
|
|
409
|
-
const [processedTxs, failedTxs] = await publicProcessor.process(txs, {
|
|
501
|
+
const [processedTxs, failedTxs] = await publicProcessor.process(txs, {
|
|
502
|
+
deadline,
|
|
503
|
+
signal: this.abortController.signal,
|
|
504
|
+
});
|
|
505
|
+
this.checkState();
|
|
410
506
|
|
|
411
507
|
if (failedTxs.length) {
|
|
412
508
|
const failedTxHashes = await Promise.all(failedTxs.map(({ tx }) => tx.getTxHash()));
|
package/src/metrics.ts
CHANGED
|
@@ -25,6 +25,12 @@ export class ProverNodeJobMetrics {
|
|
|
25
25
|
provingJobBlocks: Gauge;
|
|
26
26
|
provingJobTransactions: Gauge;
|
|
27
27
|
|
|
28
|
+
private blobProcessingDuration: Gauge;
|
|
29
|
+
private chonkVerifierDuration: Gauge;
|
|
30
|
+
private blockProcessingDuration: Histogram;
|
|
31
|
+
private checkpointProcessingDuration: Histogram;
|
|
32
|
+
private allCheckpointsProcessingDuration: Gauge;
|
|
33
|
+
|
|
28
34
|
constructor(
|
|
29
35
|
private meter: Meter,
|
|
30
36
|
public readonly tracer: Tracer,
|
|
@@ -35,6 +41,14 @@ export class ProverNodeJobMetrics {
|
|
|
35
41
|
this.provingJobCheckpoints = this.meter.createGauge(Metrics.PROVER_NODE_JOB_CHECKPOINTS);
|
|
36
42
|
this.provingJobBlocks = this.meter.createGauge(Metrics.PROVER_NODE_JOB_BLOCKS);
|
|
37
43
|
this.provingJobTransactions = this.meter.createGauge(Metrics.PROVER_NODE_JOB_TRANSACTIONS);
|
|
44
|
+
|
|
45
|
+
this.blobProcessingDuration = this.meter.createGauge(Metrics.PROVER_NODE_BLOB_PROCESSING_LAST_DURATION);
|
|
46
|
+
this.chonkVerifierDuration = this.meter.createGauge(Metrics.PROVER_NODE_CHONK_VERIFIER_LAST_DURATION);
|
|
47
|
+
this.blockProcessingDuration = this.meter.createHistogram(Metrics.PROVER_NODE_BLOCK_PROCESSING_DURATION);
|
|
48
|
+
this.checkpointProcessingDuration = this.meter.createHistogram(Metrics.PROVER_NODE_CHECKPOINT_PROCESSING_DURATION);
|
|
49
|
+
this.allCheckpointsProcessingDuration = this.meter.createGauge(
|
|
50
|
+
Metrics.PROVER_NODE_ALL_CHECKPOINTS_PROCESSING_LAST_DURATION,
|
|
51
|
+
);
|
|
38
52
|
}
|
|
39
53
|
|
|
40
54
|
public recordProvingJob(
|
|
@@ -50,6 +64,26 @@ export class ProverNodeJobMetrics {
|
|
|
50
64
|
this.provingJobBlocks.record(Math.floor(numBlocks));
|
|
51
65
|
this.provingJobTransactions.record(Math.floor(numTxs));
|
|
52
66
|
}
|
|
67
|
+
|
|
68
|
+
public recordBlobProcessing(durationMs: number) {
|
|
69
|
+
this.blobProcessingDuration.record(Math.ceil(durationMs));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
public recordChonkVerifier(durationMs: number) {
|
|
73
|
+
this.chonkVerifierDuration.record(Math.ceil(durationMs));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
public recordBlockProcessing(durationMs: number) {
|
|
77
|
+
this.blockProcessingDuration.record(Math.ceil(durationMs));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
public recordCheckpointProcessing(durationMs: number) {
|
|
81
|
+
this.checkpointProcessingDuration.record(Math.ceil(durationMs));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
public recordAllCheckpointsProcessing(durationMs: number) {
|
|
85
|
+
this.allCheckpointsProcessingDuration.record(Math.ceil(durationMs));
|
|
86
|
+
}
|
|
53
87
|
}
|
|
54
88
|
|
|
55
89
|
export class ProverNodeRewardsMetrics {
|
|
@@ -106,6 +140,13 @@ export class ProverNodeRewardsMetrics {
|
|
|
106
140
|
};
|
|
107
141
|
}
|
|
108
142
|
|
|
143
|
+
export type EstimatedSubmitProofStats = {
|
|
144
|
+
gasLimit: bigint;
|
|
145
|
+
baseFeePerGas: bigint;
|
|
146
|
+
maxPriorityFeePerGas: bigint;
|
|
147
|
+
estimatedTotalFee: bigint;
|
|
148
|
+
};
|
|
149
|
+
|
|
109
150
|
export class ProverNodePublisherMetrics {
|
|
110
151
|
gasPrice: Histogram;
|
|
111
152
|
txCount: UpDownCounter;
|
|
@@ -117,6 +158,10 @@ export class ProverNodePublisherMetrics {
|
|
|
117
158
|
txBlobDataGasCost: Histogram;
|
|
118
159
|
txTotalFee: Histogram;
|
|
119
160
|
|
|
161
|
+
private txGasEstimated: Histogram;
|
|
162
|
+
private gasPriceEstimated: Histogram;
|
|
163
|
+
private txTotalFeeEstimated: Histogram;
|
|
164
|
+
|
|
120
165
|
private senderBalance: Gauge;
|
|
121
166
|
private meter: Meter;
|
|
122
167
|
|
|
@@ -148,6 +193,12 @@ export class ProverNodePublisherMetrics {
|
|
|
148
193
|
|
|
149
194
|
this.txTotalFee = this.meter.createHistogram(Metrics.L1_PUBLISHER_TX_TOTAL_FEE);
|
|
150
195
|
|
|
196
|
+
this.txGasEstimated = this.meter.createHistogram(Metrics.PROVER_NODE_ESTIMATED_SUBMISSION_GAS);
|
|
197
|
+
|
|
198
|
+
this.gasPriceEstimated = this.meter.createHistogram(Metrics.PROVER_NODE_ESTIMATED_SUBMISSION_GAS_PRICE);
|
|
199
|
+
|
|
200
|
+
this.txTotalFeeEstimated = this.meter.createHistogram(Metrics.PROVER_NODE_ESTIMATED_SUBMISSION_TOTAL_FEE);
|
|
201
|
+
|
|
151
202
|
this.senderBalance = this.meter.createGauge(Metrics.L1_PUBLISHER_BALANCE);
|
|
152
203
|
}
|
|
153
204
|
|
|
@@ -162,6 +213,26 @@ export class ProverNodePublisherMetrics {
|
|
|
162
213
|
this.recordTx(durationMs, stats);
|
|
163
214
|
}
|
|
164
215
|
|
|
216
|
+
public recordEstimatedSubmitProof(stats: EstimatedSubmitProofStats) {
|
|
217
|
+
const attributes = { [Attributes.L1_TX_TYPE]: 'submitProof' } as const;
|
|
218
|
+
|
|
219
|
+
this.txGasEstimated.record(Number(stats.gasLimit), attributes);
|
|
220
|
+
|
|
221
|
+
try {
|
|
222
|
+
this.gasPriceEstimated.record(
|
|
223
|
+
parseInt(formatEther(stats.baseFeePerGas + stats.maxPriorityFeePerGas, 'gwei'), 10),
|
|
224
|
+
);
|
|
225
|
+
} catch {
|
|
226
|
+
// ignore
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
try {
|
|
230
|
+
this.txTotalFeeEstimated.record(parseFloat(formatEther(stats.estimatedTotalFee)));
|
|
231
|
+
} catch {
|
|
232
|
+
// ignore
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
165
236
|
public recordSenderBalance(wei: bigint, senderAddress: string) {
|
|
166
237
|
const eth = parseFloat(formatEther(wei, 'wei'));
|
|
167
238
|
this.senderBalance.record(eth, {
|