@aztec/aztec-node 0.0.1-commit.2b2662070 → 0.0.1-commit.2c0ee1788
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/aztec-node/block_response_helpers.d.ts +21 -0
- package/dest/aztec-node/block_response_helpers.d.ts.map +1 -0
- package/dest/aztec-node/block_response_helpers.js +88 -0
- package/dest/aztec-node/server.d.ts +21 -53
- package/dest/aztec-node/server.d.ts.map +1 -1
- package/dest/aztec-node/server.js +552 -336
- package/dest/sentinel/factory.d.ts +3 -2
- package/dest/sentinel/factory.d.ts.map +1 -1
- package/dest/sentinel/sentinel.d.ts +6 -3
- package/dest/sentinel/sentinel.d.ts.map +1 -1
- package/dest/sentinel/sentinel.js +7 -1
- package/package.json +26 -26
- package/src/aztec-node/block_response_helpers.ts +131 -0
- package/src/aztec-node/server.ts +647 -431
- package/src/sentinel/factory.ts +2 -1
- package/src/sentinel/sentinel.ts +17 -5
package/src/aztec-node/server.ts
CHANGED
|
@@ -50,14 +50,13 @@ import {
|
|
|
50
50
|
import { CollectionLimitsConfig, PublicSimulatorConfig } from '@aztec/stdlib/avm';
|
|
51
51
|
import { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
52
52
|
import {
|
|
53
|
-
type BlockData,
|
|
54
53
|
BlockHash,
|
|
55
54
|
type BlockParameter,
|
|
56
55
|
type DataInBlock,
|
|
57
56
|
L2Block,
|
|
58
57
|
type L2BlockSource,
|
|
58
|
+
inspectBlockParameter,
|
|
59
59
|
} from '@aztec/stdlib/block';
|
|
60
|
-
import type { PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
|
|
61
60
|
import type {
|
|
62
61
|
ContractClassPublic,
|
|
63
62
|
ContractDataSource,
|
|
@@ -67,15 +66,22 @@ import type {
|
|
|
67
66
|
} from '@aztec/stdlib/contract';
|
|
68
67
|
import { GasFees, type ManaUsageEstimate } from '@aztec/stdlib/gas';
|
|
69
68
|
import { computePublicDataTreeLeafSlot } from '@aztec/stdlib/hash';
|
|
70
|
-
import {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
69
|
+
import type {
|
|
70
|
+
AztecNode,
|
|
71
|
+
AztecNodeAdmin,
|
|
72
|
+
AztecNodeAdminConfig,
|
|
73
|
+
AztecNodeDebug,
|
|
74
|
+
BlockIncludeOptions,
|
|
75
|
+
BlockResponse,
|
|
76
|
+
ChainTip,
|
|
77
|
+
ChainTips,
|
|
78
|
+
CheckpointIncludeOptions,
|
|
79
|
+
CheckpointParameter,
|
|
80
|
+
CheckpointResponse,
|
|
81
|
+
GetContractClassLogsResponse,
|
|
82
|
+
GetPublicLogsResponse,
|
|
78
83
|
} from '@aztec/stdlib/interfaces/client';
|
|
84
|
+
import { AztecNodeAdminConfigSchema } from '@aztec/stdlib/interfaces/client';
|
|
79
85
|
import {
|
|
80
86
|
type AllowedElement,
|
|
81
87
|
type ClientProtocolCircuitVerifier,
|
|
@@ -129,6 +135,12 @@ import { createPublicClient } from 'viem';
|
|
|
129
135
|
|
|
130
136
|
import { createSentinel } from '../sentinel/factory.js';
|
|
131
137
|
import { Sentinel } from '../sentinel/sentinel.js';
|
|
138
|
+
import {
|
|
139
|
+
blockResponseFromBlockData,
|
|
140
|
+
blockResponseFromL2Block,
|
|
141
|
+
checkpointResponseFromCheckpointData,
|
|
142
|
+
checkpointResponseFromPublishedCheckpoint,
|
|
143
|
+
} from './block_response_helpers.js';
|
|
132
144
|
import { type AztecNodeConfig, createKeyStoreForValidator } from './config.js';
|
|
133
145
|
import { NodeMetrics } from './node_metrics.js';
|
|
134
146
|
|
|
@@ -157,6 +169,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
157
169
|
protected readonly slasherClient: SlasherClientInterface | undefined,
|
|
158
170
|
protected readonly validatorsSentinel: Sentinel | undefined,
|
|
159
171
|
protected readonly epochPruneWatcher: EpochPruneWatcher | undefined,
|
|
172
|
+
protected readonly attestationsBlockWatcher: AttestationsBlockWatcher | undefined,
|
|
160
173
|
protected readonly l1ChainId: number,
|
|
161
174
|
protected readonly version: number,
|
|
162
175
|
protected readonly globalVariableBuilder: GlobalVariableBuilderInterface,
|
|
@@ -196,10 +209,281 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
196
209
|
return status.syncSummary;
|
|
197
210
|
}
|
|
198
211
|
|
|
212
|
+
public async getChainTips(): Promise<ChainTips> {
|
|
213
|
+
const { proposed, checkpointed, proven, finalized } = await this.blockSource.getL2Tips();
|
|
214
|
+
return { proposed, checkpointed, proven, finalized };
|
|
215
|
+
}
|
|
216
|
+
|
|
199
217
|
public getL2Tips() {
|
|
200
218
|
return this.blockSource.getL2Tips();
|
|
201
219
|
}
|
|
202
220
|
|
|
221
|
+
public async getBlockHeader(number: BlockNumber | 'latest'): Promise<BlockHeader | undefined> {
|
|
222
|
+
const resolvedNumber = number === 'latest' ? await this.blockSource.getBlockNumber() : number;
|
|
223
|
+
if (resolvedNumber === BlockNumber.ZERO) {
|
|
224
|
+
return this.worldStateSynchronizer.getCommitted().getInitialHeader();
|
|
225
|
+
}
|
|
226
|
+
return this.blockSource.getBlockHeader(resolvedNumber);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
public async getCheckpointedBlocks(from: BlockNumber, limit: number) {
|
|
230
|
+
return (await this.blockSource.getCheckpointedBlocks(from, limit)) ?? [];
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
public getCheckpointsDataForEpoch(epoch: EpochNumber) {
|
|
234
|
+
return this.blockSource.getCheckpointsDataForEpoch(epoch);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
public getBlockNumber(tip?: ChainTip): Promise<BlockNumber> {
|
|
238
|
+
switch (tip) {
|
|
239
|
+
case undefined:
|
|
240
|
+
case 'proposed':
|
|
241
|
+
return this.blockSource.getBlockNumber();
|
|
242
|
+
case 'checkpointed':
|
|
243
|
+
return this.blockSource.getCheckpointedL2BlockNumber();
|
|
244
|
+
case 'proven':
|
|
245
|
+
return this.blockSource.getProvenBlockNumber();
|
|
246
|
+
case 'finalized':
|
|
247
|
+
return this.blockSource.getFinalizedL2BlockNumber();
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
public async getCheckpointNumber(tip?: ChainTip): Promise<CheckpointNumber> {
|
|
252
|
+
switch (tip) {
|
|
253
|
+
case undefined:
|
|
254
|
+
case 'proposed':
|
|
255
|
+
case 'checkpointed':
|
|
256
|
+
return await this.blockSource.getCheckpointNumber();
|
|
257
|
+
case 'proven':
|
|
258
|
+
case 'finalized': {
|
|
259
|
+
const tips = await this.blockSource.getL2Tips();
|
|
260
|
+
return tip === 'proven' ? tips.proven.checkpoint.number : tips.finalized.checkpoint.number;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
private isChainTip(value: unknown): value is ChainTip {
|
|
266
|
+
return value === 'proposed' || value === 'checkpointed' || value === 'proven' || value === 'finalized';
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
private async resolveBlockParameter(
|
|
270
|
+
param: BlockParameter,
|
|
271
|
+
): Promise<{ number?: BlockNumber; hash?: BlockHash; archive?: Fr }> {
|
|
272
|
+
if (BlockHash.isBlockHash(param)) {
|
|
273
|
+
return { hash: param };
|
|
274
|
+
}
|
|
275
|
+
if (typeof param === 'number') {
|
|
276
|
+
return { number: param as BlockNumber };
|
|
277
|
+
}
|
|
278
|
+
if (param === 'latest') {
|
|
279
|
+
return { number: await this.blockSource.getBlockNumber() };
|
|
280
|
+
}
|
|
281
|
+
if (this.isChainTip(param)) {
|
|
282
|
+
return { number: await this.getBlockNumber(param) };
|
|
283
|
+
}
|
|
284
|
+
if (typeof param === 'object' && param !== null) {
|
|
285
|
+
if ('number' in param) {
|
|
286
|
+
return { number: param.number };
|
|
287
|
+
}
|
|
288
|
+
if ('hash' in param) {
|
|
289
|
+
return { hash: param.hash };
|
|
290
|
+
}
|
|
291
|
+
if ('archive' in param) {
|
|
292
|
+
return { archive: param.archive };
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
throw new BadRequestError(`Invalid BlockParameter: ${JSON.stringify(param)}`);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
private async resolveCheckpointParameter(
|
|
299
|
+
param: CheckpointParameter,
|
|
300
|
+
): Promise<{ number?: CheckpointNumber; slot?: SlotNumber }> {
|
|
301
|
+
if (typeof param === 'number') {
|
|
302
|
+
return { number: param as CheckpointNumber };
|
|
303
|
+
}
|
|
304
|
+
if (param === 'latest') {
|
|
305
|
+
return { number: await this.blockSource.getCheckpointNumber() };
|
|
306
|
+
}
|
|
307
|
+
if (this.isChainTip(param)) {
|
|
308
|
+
return { number: await this.getCheckpointNumber(param) };
|
|
309
|
+
}
|
|
310
|
+
if (typeof param === 'object' && param !== null) {
|
|
311
|
+
if ('number' in param) {
|
|
312
|
+
return { number: param.number };
|
|
313
|
+
}
|
|
314
|
+
if ('slot' in param) {
|
|
315
|
+
return { slot: param.slot };
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
throw new BadRequestError(`Invalid CheckpointParameter: ${JSON.stringify(param)}`);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
public async getBlock<Opts extends BlockIncludeOptions = {}>(
|
|
322
|
+
param: BlockParameter,
|
|
323
|
+
options: Opts = {} as Opts,
|
|
324
|
+
): Promise<BlockResponse<Opts> | undefined> {
|
|
325
|
+
const resolved = await this.resolveBlockParameter(param);
|
|
326
|
+
const wantTxs = !!options.includeTransactions;
|
|
327
|
+
const wantContext = !!options.includeL1PublishInfo || !!options.includeAttestations;
|
|
328
|
+
|
|
329
|
+
if (resolved.hash !== undefined) {
|
|
330
|
+
const initial = await this.#getInitialHeaderHash();
|
|
331
|
+
if (resolved.hash.equals(initial)) {
|
|
332
|
+
return (await this.buildGenesisBlockResponse(options)) as BlockResponse<Opts>;
|
|
333
|
+
}
|
|
334
|
+
if (wantTxs) {
|
|
335
|
+
const block = await this.blockSource.getL2BlockByHash(resolved.hash);
|
|
336
|
+
if (!block) {
|
|
337
|
+
return undefined;
|
|
338
|
+
}
|
|
339
|
+
const ctx = wantContext ? await this.blockSource.getBlockDataWithCheckpointContext(block.number) : undefined;
|
|
340
|
+
return (await blockResponseFromL2Block(block, options, ctx)) as BlockResponse<Opts>;
|
|
341
|
+
}
|
|
342
|
+
const data = await this.blockSource.getBlockHeaderByHash(resolved.hash);
|
|
343
|
+
if (!data) {
|
|
344
|
+
return undefined;
|
|
345
|
+
}
|
|
346
|
+
const blockNumber = data.globalVariables.blockNumber;
|
|
347
|
+
const ctx = wantContext ? await this.blockSource.getBlockDataWithCheckpointContext(blockNumber) : undefined;
|
|
348
|
+
if (ctx) {
|
|
349
|
+
return blockResponseFromBlockData(ctx.data, blockNumber, options, ctx) as BlockResponse<Opts>;
|
|
350
|
+
}
|
|
351
|
+
const blockData = await this.blockSource.getBlockData(blockNumber);
|
|
352
|
+
if (!blockData) {
|
|
353
|
+
return undefined;
|
|
354
|
+
}
|
|
355
|
+
return blockResponseFromBlockData(blockData, blockNumber, options) as BlockResponse<Opts>;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
if (resolved.archive !== undefined) {
|
|
359
|
+
if (wantTxs) {
|
|
360
|
+
const block = await this.blockSource.getL2BlockByArchive(resolved.archive);
|
|
361
|
+
if (!block) {
|
|
362
|
+
return undefined;
|
|
363
|
+
}
|
|
364
|
+
const ctx = wantContext ? await this.blockSource.getBlockDataWithCheckpointContext(block.number) : undefined;
|
|
365
|
+
return (await blockResponseFromL2Block(block, options, ctx)) as BlockResponse<Opts>;
|
|
366
|
+
}
|
|
367
|
+
const data = await this.blockSource.getBlockDataByArchive(resolved.archive);
|
|
368
|
+
if (!data) {
|
|
369
|
+
return undefined;
|
|
370
|
+
}
|
|
371
|
+
const blockNumber = data.header.globalVariables.blockNumber;
|
|
372
|
+
const ctx = wantContext ? await this.blockSource.getBlockDataWithCheckpointContext(blockNumber) : undefined;
|
|
373
|
+
return blockResponseFromBlockData(data, blockNumber, options, ctx) as BlockResponse<Opts>;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
const blockNumber = resolved.number!;
|
|
377
|
+
if (blockNumber === BlockNumber.ZERO) {
|
|
378
|
+
return (await this.buildGenesisBlockResponse(options)) as BlockResponse<Opts>;
|
|
379
|
+
}
|
|
380
|
+
if (wantTxs) {
|
|
381
|
+
const block = await this.blockSource.getL2Block(blockNumber);
|
|
382
|
+
if (!block) {
|
|
383
|
+
return undefined;
|
|
384
|
+
}
|
|
385
|
+
const ctx = wantContext ? await this.blockSource.getBlockDataWithCheckpointContext(blockNumber) : undefined;
|
|
386
|
+
return (await blockResponseFromL2Block(block, options, ctx)) as BlockResponse<Opts>;
|
|
387
|
+
}
|
|
388
|
+
const ctx = await this.blockSource.getBlockDataWithCheckpointContext(blockNumber);
|
|
389
|
+
if (!ctx) {
|
|
390
|
+
return undefined;
|
|
391
|
+
}
|
|
392
|
+
return blockResponseFromBlockData(ctx.data, blockNumber, options, ctx) as BlockResponse<Opts>;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
public async getBlocks<Opts extends BlockIncludeOptions = {}>(
|
|
396
|
+
from: BlockNumber,
|
|
397
|
+
limit: number,
|
|
398
|
+
options: Opts = {} as Opts,
|
|
399
|
+
): Promise<BlockResponse<Opts>[]> {
|
|
400
|
+
const wantTxs = !!options.includeTransactions;
|
|
401
|
+
const wantContext = !!options.includeL1PublishInfo || !!options.includeAttestations;
|
|
402
|
+
if (wantTxs) {
|
|
403
|
+
const blocks = await this.blockSource.getBlocks(from, limit);
|
|
404
|
+
return (await Promise.all(
|
|
405
|
+
blocks.map(async block => {
|
|
406
|
+
const ctx = wantContext ? await this.blockSource.getBlockDataWithCheckpointContext(block.number) : undefined;
|
|
407
|
+
return blockResponseFromL2Block(block, options, ctx);
|
|
408
|
+
}),
|
|
409
|
+
)) as BlockResponse<Opts>[];
|
|
410
|
+
}
|
|
411
|
+
const results: BlockResponse<Opts>[] = [];
|
|
412
|
+
for (let i = 0; i < limit; i++) {
|
|
413
|
+
const blockNumber = BlockNumber(from + i);
|
|
414
|
+
const ctx = await this.blockSource.getBlockDataWithCheckpointContext(blockNumber);
|
|
415
|
+
if (!ctx) {
|
|
416
|
+
break;
|
|
417
|
+
}
|
|
418
|
+
results.push(blockResponseFromBlockData(ctx.data, blockNumber, options, ctx) as BlockResponse<Opts>);
|
|
419
|
+
}
|
|
420
|
+
return results;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
public async getCheckpoint<Opts extends CheckpointIncludeOptions = {}>(
|
|
424
|
+
param: CheckpointParameter,
|
|
425
|
+
options: Opts = {} as Opts,
|
|
426
|
+
): Promise<CheckpointResponse<Opts> | undefined> {
|
|
427
|
+
const resolved = await this.resolveCheckpointParameter(param);
|
|
428
|
+
let checkpointNumber = resolved.number;
|
|
429
|
+
if (checkpointNumber === undefined && resolved.slot !== undefined) {
|
|
430
|
+
checkpointNumber = await this.blockSource.getCheckpointNumberBySlot(resolved.slot);
|
|
431
|
+
}
|
|
432
|
+
if (checkpointNumber === undefined) {
|
|
433
|
+
return undefined;
|
|
434
|
+
}
|
|
435
|
+
if (options.includeBlocks) {
|
|
436
|
+
const [checkpoint] = await this.blockSource.getCheckpoints(checkpointNumber, 1);
|
|
437
|
+
if (!checkpoint) {
|
|
438
|
+
return undefined;
|
|
439
|
+
}
|
|
440
|
+
return (await checkpointResponseFromPublishedCheckpoint(checkpoint, options)) as CheckpointResponse<Opts>;
|
|
441
|
+
}
|
|
442
|
+
const data = await this.blockSource.getCheckpointData(checkpointNumber);
|
|
443
|
+
if (!data) {
|
|
444
|
+
return undefined;
|
|
445
|
+
}
|
|
446
|
+
return checkpointResponseFromCheckpointData(data, options) as CheckpointResponse<Opts>;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
public async getCheckpoints<Opts extends CheckpointIncludeOptions = {}>(
|
|
450
|
+
from: CheckpointNumber,
|
|
451
|
+
limit: number,
|
|
452
|
+
options: Opts = {} as Opts,
|
|
453
|
+
): Promise<CheckpointResponse<Opts>[]> {
|
|
454
|
+
if (options.includeBlocks) {
|
|
455
|
+
const checkpoints = await this.blockSource.getCheckpoints(from, limit);
|
|
456
|
+
return (await Promise.all(
|
|
457
|
+
checkpoints.map(cp => checkpointResponseFromPublishedCheckpoint(cp, options)),
|
|
458
|
+
)) as CheckpointResponse<Opts>[];
|
|
459
|
+
}
|
|
460
|
+
const datas = await this.blockSource.getCheckpointDataRange(from, limit);
|
|
461
|
+
return datas.map(d => checkpointResponseFromCheckpointData(d, options)) as CheckpointResponse<Opts>[];
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
private async buildGenesisBlockResponse(options: BlockIncludeOptions): Promise<BlockResponse> {
|
|
465
|
+
const initial = this.worldStateSynchronizer.getCommitted().getInitialHeader();
|
|
466
|
+
const empty = L2Block.empty(initial);
|
|
467
|
+
const response: BlockResponse = {
|
|
468
|
+
header: empty.header,
|
|
469
|
+
archive: empty.archive,
|
|
470
|
+
hash: await this.#getInitialHeaderHash(),
|
|
471
|
+
checkpointNumber: empty.checkpointNumber,
|
|
472
|
+
indexWithinCheckpoint: empty.indexWithinCheckpoint,
|
|
473
|
+
number: empty.number,
|
|
474
|
+
};
|
|
475
|
+
if (options.includeTransactions) {
|
|
476
|
+
(response as BlockResponse).body = empty.body;
|
|
477
|
+
}
|
|
478
|
+
if (options.includeL1PublishInfo) {
|
|
479
|
+
(response as BlockResponse).l1 = { published: false };
|
|
480
|
+
}
|
|
481
|
+
if (options.includeAttestations) {
|
|
482
|
+
(response as BlockResponse).attestations = [];
|
|
483
|
+
}
|
|
484
|
+
return response;
|
|
485
|
+
}
|
|
486
|
+
|
|
203
487
|
/**
|
|
204
488
|
* initializes the Aztec Node, wait for component to sync.
|
|
205
489
|
* @param config - The configuration to be used by the aztec node.
|
|
@@ -313,326 +597,354 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
313
597
|
|
|
314
598
|
const epochCache = await EpochCache.create(config.l1Contracts.rollupAddress, config, { dateProvider });
|
|
315
599
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
const worldStateSynchronizer = await createWorldStateSynchronizer(config, archiver, options.genesis, telemetry);
|
|
324
|
-
const useRealVerifiers = config.realProofs || config.debugForceTxProofVerification;
|
|
325
|
-
let peerProofVerifier: ClientProtocolCircuitVerifier;
|
|
326
|
-
let rpcProofVerifier: ClientProtocolCircuitVerifier;
|
|
327
|
-
if (useRealVerifiers) {
|
|
328
|
-
peerProofVerifier = await BatchChonkVerifier.new(config, config.bbChonkVerifyMaxBatch, 'peer');
|
|
329
|
-
const rpcVerifier = await BBCircuitVerifier.new(config);
|
|
330
|
-
rpcProofVerifier = new QueuedIVCVerifier(rpcVerifier, config.numConcurrentIVCVerifiers);
|
|
331
|
-
} else {
|
|
332
|
-
peerProofVerifier = new TestCircuitVerifier(config.proverTestVerificationDelayMs);
|
|
333
|
-
rpcProofVerifier = new TestCircuitVerifier(config.proverTestVerificationDelayMs);
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
let debugLogStore: DebugLogStore;
|
|
337
|
-
if (!config.realProofs) {
|
|
338
|
-
log.warn(`Aztec node is accepting fake proofs`);
|
|
339
|
-
|
|
340
|
-
debugLogStore = new InMemoryDebugLogStore();
|
|
341
|
-
log.info(
|
|
342
|
-
'Aztec node started in test mode (realProofs set to false) hence debug logs from public functions will be collected and served',
|
|
600
|
+
// Track started resources so we can clean up on partial failure during node creation.
|
|
601
|
+
const started: { stop?(): Promise<void> | void }[] = [];
|
|
602
|
+
try {
|
|
603
|
+
const archiver = await createArchiver(
|
|
604
|
+
config,
|
|
605
|
+
{ blobClient, epochCache, telemetry, dateProvider },
|
|
606
|
+
{ blockUntilSync: !config.skipArchiverInitialSync },
|
|
343
607
|
);
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
608
|
+
started.push(archiver);
|
|
609
|
+
|
|
610
|
+
// now create the merkle trees and the world state synchronizer
|
|
611
|
+
const worldStateSynchronizer = await createWorldStateSynchronizer(config, archiver, options.genesis, telemetry);
|
|
612
|
+
started.push(worldStateSynchronizer);
|
|
613
|
+
const useRealVerifiers = config.realProofs || config.debugForceTxProofVerification;
|
|
614
|
+
let peerProofVerifier: ClientProtocolCircuitVerifier;
|
|
615
|
+
let rpcProofVerifier: ClientProtocolCircuitVerifier;
|
|
616
|
+
if (useRealVerifiers) {
|
|
617
|
+
peerProofVerifier = await BatchChonkVerifier.new(config, config.bbChonkVerifyMaxBatch, 'peer');
|
|
618
|
+
const rpcVerifier = await BBCircuitVerifier.new(config);
|
|
619
|
+
rpcProofVerifier = new QueuedIVCVerifier(rpcVerifier, config.numConcurrentIVCVerifiers);
|
|
620
|
+
} else {
|
|
621
|
+
peerProofVerifier = new TestCircuitVerifier(config.proverTestVerificationDelayMs);
|
|
622
|
+
rpcProofVerifier = new TestCircuitVerifier(config.proverTestVerificationDelayMs);
|
|
623
|
+
}
|
|
624
|
+
started.push(peerProofVerifier, rpcProofVerifier);
|
|
352
625
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
archiver,
|
|
357
|
-
peerProofVerifier,
|
|
358
|
-
worldStateSynchronizer,
|
|
359
|
-
epochCache,
|
|
360
|
-
packageVersion,
|
|
361
|
-
dateProvider,
|
|
362
|
-
telemetry,
|
|
363
|
-
deps.p2pClientDeps,
|
|
364
|
-
);
|
|
626
|
+
let debugLogStore: DebugLogStore;
|
|
627
|
+
if (!config.realProofs) {
|
|
628
|
+
log.warn(`Aztec node is accepting fake proofs`);
|
|
365
629
|
|
|
366
|
-
|
|
367
|
-
|
|
630
|
+
debugLogStore = new InMemoryDebugLogStore();
|
|
631
|
+
log.info(
|
|
632
|
+
'Aztec node started in test mode (realProofs set to false) hence debug logs from public functions will be collected and served',
|
|
633
|
+
);
|
|
634
|
+
} else {
|
|
635
|
+
debugLogStore = new NullDebugLogStore();
|
|
636
|
+
}
|
|
368
637
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
...config,
|
|
638
|
+
const globalVariableBuilderConfig = {
|
|
639
|
+
l1Contracts: config.l1Contracts,
|
|
640
|
+
ethereumSlotDuration: config.ethereumSlotDuration,
|
|
641
|
+
rollupVersion: BigInt(config.rollupVersion),
|
|
374
642
|
l1GenesisTime,
|
|
375
643
|
slotDuration: Number(slotDuration),
|
|
376
|
-
|
|
377
|
-
maxTxsPerCheckpoint: config.validateMaxTxsPerCheckpoint,
|
|
378
|
-
},
|
|
379
|
-
worldStateSynchronizer,
|
|
380
|
-
archiver,
|
|
381
|
-
dateProvider,
|
|
382
|
-
telemetry,
|
|
383
|
-
);
|
|
384
|
-
|
|
385
|
-
let validatorClient: ValidatorClient | undefined;
|
|
386
|
-
|
|
387
|
-
if (!proverOnly) {
|
|
388
|
-
// Create validator client if required
|
|
389
|
-
validatorClient = await createValidatorClient(config, {
|
|
390
|
-
checkpointsBuilder: validatorCheckpointsBuilder,
|
|
391
|
-
worldState: worldStateSynchronizer,
|
|
392
|
-
p2pClient,
|
|
393
|
-
telemetry,
|
|
394
|
-
dateProvider,
|
|
395
|
-
epochCache,
|
|
396
|
-
blockSource: archiver,
|
|
397
|
-
l1ToL2MessageSource: archiver,
|
|
398
|
-
keyStoreManager,
|
|
399
|
-
blobClient,
|
|
400
|
-
slashingProtectionDb: deps.slashingProtectionDb,
|
|
401
|
-
});
|
|
402
|
-
|
|
403
|
-
// If we have a validator client, register it as a source of offenses for the slasher,
|
|
404
|
-
// and have it register callbacks on the p2p client *before* we start it, otherwise messages
|
|
405
|
-
// like attestations or auths will fail.
|
|
406
|
-
if (validatorClient) {
|
|
407
|
-
watchers.push(validatorClient);
|
|
644
|
+
};
|
|
408
645
|
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
validatorClient.getProposalHandler().register(p2pClient, true, archiver, getValidatorAddresses);
|
|
646
|
+
const globalVariableBuilder = new GlobalVariableBuilder(dateProvider, publicClient, globalVariableBuilderConfig);
|
|
647
|
+
const feeProvider = new FeeProviderImpl(dateProvider, publicClient, globalVariableBuilderConfig);
|
|
412
648
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
649
|
+
const proverOnly = config.enableProverNode && config.disableValidator;
|
|
650
|
+
if (proverOnly) {
|
|
651
|
+
log.info('Starting in prover-only mode: skipping validator, sequencer, sentinel, and slasher subsystems');
|
|
416
652
|
}
|
|
417
|
-
}
|
|
418
653
|
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
log.info(`Setting up proposal handler` + (reexecute ? ' with reexecution of proposals' : ''));
|
|
426
|
-
createProposalHandler(config, {
|
|
427
|
-
checkpointsBuilder: validatorCheckpointsBuilder,
|
|
428
|
-
worldState: worldStateSynchronizer,
|
|
654
|
+
// create the tx pool and the p2p client, which will need the l2 block source
|
|
655
|
+
const p2pClient = await createP2PClient(
|
|
656
|
+
config,
|
|
657
|
+
archiver,
|
|
658
|
+
peerProofVerifier,
|
|
659
|
+
worldStateSynchronizer,
|
|
429
660
|
epochCache,
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
p2pClient,
|
|
433
|
-
blobClient,
|
|
661
|
+
feeProvider,
|
|
662
|
+
packageVersion,
|
|
434
663
|
dateProvider,
|
|
435
664
|
telemetry,
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
665
|
+
deps.p2pClientDeps,
|
|
666
|
+
);
|
|
667
|
+
started.push(p2pClient);
|
|
668
|
+
|
|
669
|
+
// We'll accumulate sentinel watchers here
|
|
670
|
+
const watchers: Watcher[] = [];
|
|
671
|
+
|
|
672
|
+
// Create FullNodeCheckpointsBuilder for block proposal handling and tx validation.
|
|
673
|
+
// Override maxTxsPerCheckpoint with the validator-specific limit if set.
|
|
674
|
+
const validatorCheckpointsBuilder = new FullNodeCheckpointsBuilder(
|
|
675
|
+
{
|
|
676
|
+
...config,
|
|
677
|
+
l1GenesisTime,
|
|
678
|
+
slotDuration: Number(slotDuration),
|
|
679
|
+
rollupManaLimit,
|
|
680
|
+
maxTxsPerCheckpoint: config.validateMaxTxsPerCheckpoint,
|
|
681
|
+
},
|
|
682
|
+
worldStateSynchronizer,
|
|
683
|
+
archiver,
|
|
684
|
+
dateProvider,
|
|
685
|
+
telemetry,
|
|
686
|
+
);
|
|
444
687
|
|
|
445
|
-
|
|
446
|
-
let epochPruneWatcher: EpochPruneWatcher | undefined;
|
|
447
|
-
let attestationsBlockWatcher: AttestationsBlockWatcher | undefined;
|
|
688
|
+
let validatorClient: ValidatorClient | undefined;
|
|
448
689
|
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
690
|
+
if (!config.disableValidator) {
|
|
691
|
+
// Create validator client if required
|
|
692
|
+
validatorClient = await createValidatorClient(config, {
|
|
693
|
+
checkpointsBuilder: validatorCheckpointsBuilder,
|
|
694
|
+
worldState: worldStateSynchronizer,
|
|
695
|
+
p2pClient,
|
|
696
|
+
telemetry,
|
|
697
|
+
dateProvider,
|
|
698
|
+
epochCache,
|
|
699
|
+
blockSource: archiver,
|
|
700
|
+
l1ToL2MessageSource: archiver,
|
|
701
|
+
keyStoreManager,
|
|
702
|
+
blobClient,
|
|
703
|
+
slashingProtectionDb: deps.slashingProtectionDb,
|
|
704
|
+
});
|
|
705
|
+
|
|
706
|
+
// If we have a validator client, register it as a source of offenses for the slasher,
|
|
707
|
+
// and have it register callbacks on the p2p client *before* we start it, otherwise messages
|
|
708
|
+
// like attestations or auths will fail.
|
|
709
|
+
if (validatorClient) {
|
|
710
|
+
watchers.push(validatorClient);
|
|
711
|
+
|
|
712
|
+
const vc = validatorClient;
|
|
713
|
+
const getValidatorAddresses = () => vc.getValidatorAddresses().map(a => a.toString());
|
|
714
|
+
validatorClient.getProposalHandler().register(p2pClient, true, archiver, getValidatorAddresses);
|
|
715
|
+
|
|
716
|
+
if (!options.dontStartSequencer) {
|
|
717
|
+
await validatorClient.registerHandlers();
|
|
718
|
+
}
|
|
719
|
+
}
|
|
453
720
|
}
|
|
454
721
|
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
722
|
+
// If there's no validator client, create a ProposalHandler to handle block and checkpoint proposals
|
|
723
|
+
// for monitoring or reexecution. Reexecution (default) allows us to follow the pending chain,
|
|
724
|
+
// while non-reexecution is used for validating the proposals and collecting their txs.
|
|
725
|
+
// Checkpoint proposals rebuild blobs if the blob client can upload blobs.
|
|
726
|
+
if (!validatorClient) {
|
|
727
|
+
const reexecute = !!config.alwaysReexecuteBlockProposals;
|
|
728
|
+
log.info(`Setting up proposal handler` + (reexecute ? ' with reexecution of proposals' : ''));
|
|
729
|
+
createProposalHandler(config, {
|
|
730
|
+
checkpointsBuilder: validatorCheckpointsBuilder,
|
|
731
|
+
worldState: worldStateSynchronizer,
|
|
459
732
|
epochCache,
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
733
|
+
blockSource: archiver,
|
|
734
|
+
l1ToL2MessageSource: archiver,
|
|
735
|
+
p2pClient,
|
|
736
|
+
blobClient,
|
|
737
|
+
dateProvider,
|
|
738
|
+
telemetry,
|
|
739
|
+
}).register(p2pClient, reexecute, archiver);
|
|
465
740
|
}
|
|
466
741
|
|
|
467
|
-
//
|
|
468
|
-
|
|
469
|
-
attestationsBlockWatcher = new AttestationsBlockWatcher(archiver, epochCache, config);
|
|
470
|
-
watchers.push(attestationsBlockWatcher);
|
|
471
|
-
}
|
|
472
|
-
}
|
|
742
|
+
// Start world state and wait for it to sync to the archiver.
|
|
743
|
+
await worldStateSynchronizer.start();
|
|
473
744
|
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
.waitForInitialSync()
|
|
477
|
-
.then(async () => {
|
|
478
|
-
await validatorsSentinel?.start();
|
|
479
|
-
await epochPruneWatcher?.start();
|
|
480
|
-
await attestationsBlockWatcher?.start();
|
|
481
|
-
log.info(`All p2p services started`);
|
|
482
|
-
})
|
|
483
|
-
.catch(err => log.error('Failed to start p2p services after archiver sync', err));
|
|
484
|
-
|
|
485
|
-
const globalVariableBuilderConfig = {
|
|
486
|
-
l1Contracts: config.l1Contracts,
|
|
487
|
-
ethereumSlotDuration: config.ethereumSlotDuration,
|
|
488
|
-
rollupVersion: BigInt(config.rollupVersion),
|
|
489
|
-
l1GenesisTime,
|
|
490
|
-
slotDuration: Number(slotDuration),
|
|
491
|
-
};
|
|
745
|
+
// Start p2p. Note that it depends on world state to be running.
|
|
746
|
+
await p2pClient.start();
|
|
492
747
|
|
|
493
|
-
|
|
494
|
-
|
|
748
|
+
let validatorsSentinel: Awaited<ReturnType<typeof createSentinel>> | undefined;
|
|
749
|
+
let epochPruneWatcher: EpochPruneWatcher | undefined;
|
|
750
|
+
let attestationsBlockWatcher: AttestationsBlockWatcher | undefined;
|
|
495
751
|
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
// as they are executed when the node is selected as proposer.
|
|
502
|
-
const validatorAddresses = keyStoreManager
|
|
503
|
-
? NodeKeystoreAdapter.fromKeyStoreManager(keyStoreManager).getAddresses()
|
|
504
|
-
: [];
|
|
752
|
+
if (!proverOnly) {
|
|
753
|
+
validatorsSentinel = await createSentinel(epochCache, archiver, p2pClient, config);
|
|
754
|
+
if (validatorsSentinel && config.slashInactivityPenalty > 0n) {
|
|
755
|
+
watchers.push(validatorsSentinel);
|
|
756
|
+
}
|
|
505
757
|
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
758
|
+
if (config.slashPrunePenalty > 0n || config.slashDataWithholdingPenalty > 0n) {
|
|
759
|
+
epochPruneWatcher = new EpochPruneWatcher(
|
|
760
|
+
archiver,
|
|
761
|
+
archiver,
|
|
762
|
+
epochCache,
|
|
763
|
+
p2pClient.getTxProvider(),
|
|
764
|
+
validatorCheckpointsBuilder,
|
|
765
|
+
config,
|
|
766
|
+
);
|
|
767
|
+
watchers.push(epochPruneWatcher);
|
|
768
|
+
}
|
|
517
769
|
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
770
|
+
// We assume we want to slash for invalid attestations unless all max penalties are set to 0
|
|
771
|
+
if (config.slashProposeInvalidAttestationsPenalty > 0n || config.slashAttestDescendantOfInvalidPenalty > 0n) {
|
|
772
|
+
attestationsBlockWatcher = new AttestationsBlockWatcher(archiver, epochCache, config);
|
|
773
|
+
watchers.push(attestationsBlockWatcher);
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
// Start p2p-related services once the archiver has completed sync
|
|
778
|
+
void archiver
|
|
779
|
+
.waitForInitialSync()
|
|
780
|
+
.then(async () => {
|
|
781
|
+
if (validatorsSentinel) {
|
|
782
|
+
await validatorsSentinel.start();
|
|
783
|
+
started.push(validatorsSentinel);
|
|
784
|
+
}
|
|
785
|
+
if (epochPruneWatcher) {
|
|
786
|
+
await epochPruneWatcher.start();
|
|
787
|
+
started.push(epochPruneWatcher);
|
|
788
|
+
}
|
|
789
|
+
if (attestationsBlockWatcher) {
|
|
790
|
+
await attestationsBlockWatcher.start();
|
|
791
|
+
started.push(attestationsBlockWatcher);
|
|
792
|
+
}
|
|
793
|
+
log.info(`All p2p services started`);
|
|
794
|
+
})
|
|
795
|
+
.catch(err => log.error('Failed to start p2p services after archiver sync', err));
|
|
796
|
+
|
|
797
|
+
// Validator enabled, create/start relevant service
|
|
798
|
+
let sequencer: SequencerClient | undefined;
|
|
799
|
+
let slasherClient: SlasherClientInterface | undefined;
|
|
800
|
+
if (!config.disableValidator && validatorClient) {
|
|
801
|
+
// We create a slasher only if we have a sequencer, since all slashing actions go through the sequencer publisher
|
|
802
|
+
// as they are executed when the node is selected as proposer.
|
|
803
|
+
const validatorAddresses = keyStoreManager
|
|
804
|
+
? NodeKeystoreAdapter.fromKeyStoreManager(keyStoreManager).getAddresses()
|
|
805
|
+
: [];
|
|
806
|
+
|
|
807
|
+
slasherClient = await createSlasher(
|
|
808
|
+
config,
|
|
809
|
+
config.l1Contracts,
|
|
810
|
+
getPublicClient(config),
|
|
811
|
+
watchers,
|
|
812
|
+
dateProvider,
|
|
813
|
+
epochCache,
|
|
814
|
+
validatorAddresses,
|
|
815
|
+
undefined, // logger
|
|
816
|
+
);
|
|
817
|
+
await slasherClient.start();
|
|
818
|
+
started.push(slasherClient);
|
|
819
|
+
|
|
820
|
+
const l1TxUtils = config.sequencerPublisherForwarderAddress
|
|
821
|
+
? await createForwarderL1TxUtilsFromSigners(
|
|
822
|
+
publicClient,
|
|
823
|
+
keyStoreManager!.createAllValidatorPublisherSigners(),
|
|
824
|
+
config.sequencerPublisherForwarderAddress,
|
|
825
|
+
{ ...config, scope: 'sequencer' },
|
|
826
|
+
{ telemetry, logger: log.createChild('l1-tx-utils'), dateProvider, kzg: Blob.getViemKzgInstance() },
|
|
827
|
+
)
|
|
828
|
+
: await createL1TxUtilsFromSigners(
|
|
829
|
+
publicClient,
|
|
830
|
+
keyStoreManager!.createAllValidatorPublisherSigners(),
|
|
831
|
+
{ ...config, scope: 'sequencer' },
|
|
832
|
+
{ telemetry, logger: log.createChild('l1-tx-utils'), dateProvider, kzg: Blob.getViemKzgInstance() },
|
|
833
|
+
);
|
|
834
|
+
|
|
835
|
+
// Create a funder L1TxUtils from the keystore funding account (if configured)
|
|
836
|
+
const fundingSigner = keyStoreManager?.createFundingSigner();
|
|
837
|
+
let funderL1TxUtils: L1TxUtils | undefined;
|
|
838
|
+
if (fundingSigner) {
|
|
839
|
+
const [funder] = await createL1TxUtilsFromSigners(
|
|
527
840
|
publicClient,
|
|
528
|
-
|
|
841
|
+
[fundingSigner],
|
|
529
842
|
{ ...config, scope: 'sequencer' },
|
|
530
|
-
{ telemetry, logger: log.createChild('l1-tx-utils'), dateProvider
|
|
843
|
+
{ telemetry, logger: log.createChild('l1-tx-utils:funder'), dateProvider },
|
|
531
844
|
);
|
|
845
|
+
funderL1TxUtils = funder;
|
|
846
|
+
}
|
|
532
847
|
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
{ telemetry, logger: log.createChild('l1-tx-utils:funder'), dateProvider },
|
|
848
|
+
// Create and start the sequencer client
|
|
849
|
+
const checkpointsBuilder = new CheckpointsBuilder(
|
|
850
|
+
{ ...config, l1GenesisTime, slotDuration: Number(slotDuration), rollupManaLimit },
|
|
851
|
+
worldStateSynchronizer,
|
|
852
|
+
archiver,
|
|
853
|
+
dateProvider,
|
|
854
|
+
telemetry,
|
|
855
|
+
debugLogStore,
|
|
542
856
|
);
|
|
543
|
-
|
|
857
|
+
|
|
858
|
+
sequencer = await SequencerClient.new(config, {
|
|
859
|
+
...deps,
|
|
860
|
+
epochCache,
|
|
861
|
+
l1TxUtils,
|
|
862
|
+
funderL1TxUtils,
|
|
863
|
+
validatorClient,
|
|
864
|
+
p2pClient,
|
|
865
|
+
worldStateSynchronizer,
|
|
866
|
+
slasherClient,
|
|
867
|
+
checkpointsBuilder,
|
|
868
|
+
l2BlockSource: archiver,
|
|
869
|
+
l1ToL2MessageSource: archiver,
|
|
870
|
+
telemetry,
|
|
871
|
+
dateProvider,
|
|
872
|
+
blobClient,
|
|
873
|
+
nodeKeyStore: keyStoreManager!,
|
|
874
|
+
globalVariableBuilder,
|
|
875
|
+
});
|
|
544
876
|
}
|
|
545
877
|
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
debugLogStore,
|
|
554
|
-
);
|
|
878
|
+
if (!options.dontStartSequencer && sequencer) {
|
|
879
|
+
await sequencer.start();
|
|
880
|
+
started.push(sequencer);
|
|
881
|
+
log.verbose(`Sequencer started`);
|
|
882
|
+
} else if (sequencer) {
|
|
883
|
+
log.warn(`Sequencer created but not started`);
|
|
884
|
+
}
|
|
555
885
|
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
886
|
+
// Create prover node subsystem if enabled
|
|
887
|
+
let proverNode: ProverNode | undefined;
|
|
888
|
+
if (config.enableProverNode) {
|
|
889
|
+
proverNode = await createProverNode(config, {
|
|
890
|
+
...deps.proverNodeDeps,
|
|
891
|
+
telemetry,
|
|
892
|
+
dateProvider,
|
|
893
|
+
archiver,
|
|
894
|
+
worldStateSynchronizer,
|
|
895
|
+
p2pClient,
|
|
896
|
+
epochCache,
|
|
897
|
+
blobClient,
|
|
898
|
+
keyStoreManager,
|
|
899
|
+
});
|
|
900
|
+
|
|
901
|
+
if (!options.dontStartProverNode) {
|
|
902
|
+
await proverNode.start();
|
|
903
|
+
started.push(proverNode);
|
|
904
|
+
log.info(`Prover node subsystem started`);
|
|
905
|
+
} else {
|
|
906
|
+
log.info(`Prover node subsystem created but not started`);
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
const node = new AztecNodeService(
|
|
911
|
+
config,
|
|
562
912
|
p2pClient,
|
|
913
|
+
archiver,
|
|
914
|
+
archiver,
|
|
915
|
+
archiver,
|
|
916
|
+
archiver,
|
|
563
917
|
worldStateSynchronizer,
|
|
918
|
+
sequencer,
|
|
919
|
+
proverNode,
|
|
564
920
|
slasherClient,
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
blobClient,
|
|
571
|
-
nodeKeyStore: keyStoreManager!,
|
|
921
|
+
validatorsSentinel,
|
|
922
|
+
epochPruneWatcher,
|
|
923
|
+
attestationsBlockWatcher,
|
|
924
|
+
ethereumChain.chainInfo.id,
|
|
925
|
+
config.rollupVersion,
|
|
572
926
|
globalVariableBuilder,
|
|
573
|
-
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
if (!options.dontStartSequencer && sequencer) {
|
|
577
|
-
await sequencer.start();
|
|
578
|
-
log.verbose(`Sequencer started`);
|
|
579
|
-
} else if (sequencer) {
|
|
580
|
-
log.warn(`Sequencer created but not started`);
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
// Create prover node subsystem if enabled
|
|
584
|
-
let proverNode: ProverNode | undefined;
|
|
585
|
-
if (config.enableProverNode) {
|
|
586
|
-
proverNode = await createProverNode(config, {
|
|
587
|
-
...deps.proverNodeDeps,
|
|
588
|
-
telemetry,
|
|
589
|
-
dateProvider,
|
|
590
|
-
archiver,
|
|
591
|
-
worldStateSynchronizer,
|
|
592
|
-
p2pClient,
|
|
927
|
+
feeProvider,
|
|
593
928
|
epochCache,
|
|
929
|
+
packageVersion,
|
|
930
|
+
peerProofVerifier,
|
|
931
|
+
rpcProofVerifier,
|
|
932
|
+
telemetry,
|
|
933
|
+
log,
|
|
594
934
|
blobClient,
|
|
935
|
+
validatorClient,
|
|
595
936
|
keyStoreManager,
|
|
596
|
-
|
|
937
|
+
debugLogStore,
|
|
938
|
+
);
|
|
597
939
|
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
940
|
+
return node;
|
|
941
|
+
} catch (err) {
|
|
942
|
+
log.error('Failed during node creation, stopping started resources', err);
|
|
943
|
+
for (const resource of started.reverse()) {
|
|
944
|
+
await tryStop(resource);
|
|
603
945
|
}
|
|
946
|
+
throw err;
|
|
604
947
|
}
|
|
605
|
-
|
|
606
|
-
const node = new AztecNodeService(
|
|
607
|
-
config,
|
|
608
|
-
p2pClient,
|
|
609
|
-
archiver,
|
|
610
|
-
archiver,
|
|
611
|
-
archiver,
|
|
612
|
-
archiver,
|
|
613
|
-
worldStateSynchronizer,
|
|
614
|
-
sequencer,
|
|
615
|
-
proverNode,
|
|
616
|
-
slasherClient,
|
|
617
|
-
validatorsSentinel,
|
|
618
|
-
epochPruneWatcher,
|
|
619
|
-
ethereumChain.chainInfo.id,
|
|
620
|
-
config.rollupVersion,
|
|
621
|
-
globalVariableBuilder,
|
|
622
|
-
feeProvider,
|
|
623
|
-
epochCache,
|
|
624
|
-
packageVersion,
|
|
625
|
-
peerProofVerifier,
|
|
626
|
-
rpcProofVerifier,
|
|
627
|
-
telemetry,
|
|
628
|
-
log,
|
|
629
|
-
blobClient,
|
|
630
|
-
validatorClient,
|
|
631
|
-
keyStoreManager,
|
|
632
|
-
debugLogStore,
|
|
633
|
-
);
|
|
634
|
-
|
|
635
|
-
return node;
|
|
636
948
|
}
|
|
637
949
|
|
|
638
950
|
/**
|
|
@@ -707,71 +1019,6 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
707
1019
|
return nodeInfo;
|
|
708
1020
|
}
|
|
709
1021
|
|
|
710
|
-
/**
|
|
711
|
-
* Get a block specified by its block number, block hash, or 'latest'.
|
|
712
|
-
* @param block - The block parameter (block number, block hash, or 'latest').
|
|
713
|
-
* @returns The requested block.
|
|
714
|
-
*/
|
|
715
|
-
public async getBlock(block: BlockParameter): Promise<L2Block | undefined> {
|
|
716
|
-
if (BlockHash.isBlockHash(block)) {
|
|
717
|
-
return this.getBlockByHash(block);
|
|
718
|
-
}
|
|
719
|
-
const blockNumber = block === 'latest' ? await this.getBlockNumber() : (block as BlockNumber);
|
|
720
|
-
if (blockNumber === BlockNumber.ZERO) {
|
|
721
|
-
return this.buildInitialBlock();
|
|
722
|
-
}
|
|
723
|
-
return await this.blockSource.getL2Block(blockNumber);
|
|
724
|
-
}
|
|
725
|
-
|
|
726
|
-
/**
|
|
727
|
-
* Get a block specified by its hash.
|
|
728
|
-
* @param blockHash - The block hash being requested.
|
|
729
|
-
* @returns The requested block.
|
|
730
|
-
*/
|
|
731
|
-
public async getBlockByHash(blockHash: BlockHash): Promise<L2Block | undefined> {
|
|
732
|
-
const initialBlockHash = await this.#getInitialHeaderHash();
|
|
733
|
-
if (blockHash.equals(initialBlockHash)) {
|
|
734
|
-
return this.buildInitialBlock();
|
|
735
|
-
}
|
|
736
|
-
return await this.blockSource.getL2BlockByHash(blockHash);
|
|
737
|
-
}
|
|
738
|
-
|
|
739
|
-
private buildInitialBlock(): L2Block {
|
|
740
|
-
const initialHeader = this.worldStateSynchronizer.getCommitted().getInitialHeader();
|
|
741
|
-
return L2Block.empty(initialHeader);
|
|
742
|
-
}
|
|
743
|
-
|
|
744
|
-
/**
|
|
745
|
-
* Get a block specified by its archive root.
|
|
746
|
-
* @param archive - The archive root being requested.
|
|
747
|
-
* @returns The requested block.
|
|
748
|
-
*/
|
|
749
|
-
public async getBlockByArchive(archive: Fr): Promise<L2Block | undefined> {
|
|
750
|
-
return await this.blockSource.getL2BlockByArchive(archive);
|
|
751
|
-
}
|
|
752
|
-
|
|
753
|
-
/**
|
|
754
|
-
* Method to request blocks. Will attempt to return all requested blocks but will return only those available.
|
|
755
|
-
* @param from - The start of the range of blocks to return.
|
|
756
|
-
* @param limit - The maximum number of blocks to obtain.
|
|
757
|
-
* @returns The blocks requested.
|
|
758
|
-
*/
|
|
759
|
-
public async getBlocks(from: BlockNumber, limit: number): Promise<L2Block[]> {
|
|
760
|
-
return (await this.blockSource.getBlocks(from, BlockNumber(limit))) ?? [];
|
|
761
|
-
}
|
|
762
|
-
|
|
763
|
-
public async getCheckpoints(from: CheckpointNumber, limit: number): Promise<PublishedCheckpoint[]> {
|
|
764
|
-
return (await this.blockSource.getCheckpoints(from, limit)) ?? [];
|
|
765
|
-
}
|
|
766
|
-
|
|
767
|
-
public async getCheckpointedBlocks(from: BlockNumber, limit: number) {
|
|
768
|
-
return (await this.blockSource.getCheckpointedBlocks(from, limit)) ?? [];
|
|
769
|
-
}
|
|
770
|
-
|
|
771
|
-
public getCheckpointsDataForEpoch(epochNumber: EpochNumber) {
|
|
772
|
-
return this.blockSource.getCheckpointsDataForEpoch(epochNumber);
|
|
773
|
-
}
|
|
774
|
-
|
|
775
1022
|
public async getCurrentMinFees(): Promise<GasFees> {
|
|
776
1023
|
return await this.feeProvider.getCurrentMinFees();
|
|
777
1024
|
}
|
|
@@ -789,26 +1036,6 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
789
1036
|
return GasFees.from({ feePerDaGas: 0n, feePerL2Gas: 0n });
|
|
790
1037
|
}
|
|
791
1038
|
|
|
792
|
-
/**
|
|
793
|
-
* Method to fetch the latest block number synchronized by the node.
|
|
794
|
-
* @returns The block number.
|
|
795
|
-
*/
|
|
796
|
-
public async getBlockNumber(): Promise<BlockNumber> {
|
|
797
|
-
return await this.blockSource.getBlockNumber();
|
|
798
|
-
}
|
|
799
|
-
|
|
800
|
-
public async getProvenBlockNumber(): Promise<BlockNumber> {
|
|
801
|
-
return await this.blockSource.getProvenBlockNumber();
|
|
802
|
-
}
|
|
803
|
-
|
|
804
|
-
public async getCheckpointedBlockNumber(): Promise<BlockNumber> {
|
|
805
|
-
return await this.blockSource.getCheckpointedL2BlockNumber();
|
|
806
|
-
}
|
|
807
|
-
|
|
808
|
-
public getCheckpointNumber(): Promise<CheckpointNumber> {
|
|
809
|
-
return this.blockSource.getCheckpointNumber();
|
|
810
|
-
}
|
|
811
|
-
|
|
812
1039
|
/**
|
|
813
1040
|
* Method to fetch the version of the package.
|
|
814
1041
|
* @returns The node package version
|
|
@@ -926,7 +1153,13 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
926
1153
|
throw new Error(`Invalid tx: ${reason}`);
|
|
927
1154
|
}
|
|
928
1155
|
|
|
929
|
-
|
|
1156
|
+
try {
|
|
1157
|
+
await this.p2pClient!.sendTx(tx);
|
|
1158
|
+
} catch (err) {
|
|
1159
|
+
this.metrics.receivedTx(timer.ms(), false);
|
|
1160
|
+
this.log.warn(`Mempool rejected tx ${txHash}: ${(err as Error).message}`, { txHash });
|
|
1161
|
+
throw err;
|
|
1162
|
+
}
|
|
930
1163
|
const duration = timer.ms();
|
|
931
1164
|
this.metrics.receivedTx(duration, true);
|
|
932
1165
|
this.log.info(`Received tx ${txHash} in ${duration}ms`, { txHash });
|
|
@@ -948,10 +1181,15 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
948
1181
|
// If the tx is in the pool but not in the archiver, it's pending.
|
|
949
1182
|
// This handles race conditions between archiver and p2p, where the archiver
|
|
950
1183
|
// has pruned the block in which a tx was mined, but p2p has not caught up yet.
|
|
951
|
-
receipt = new TxReceipt(txHash, TxStatus.PENDING, undefined, undefined);
|
|
1184
|
+
receipt = new TxReceipt(txHash, TxStatus.PENDING, /*executionResult=*/ undefined, /*error=*/ undefined);
|
|
952
1185
|
} else {
|
|
953
1186
|
// Otherwise, if we don't know the tx, we consider it dropped.
|
|
954
|
-
receipt = new TxReceipt(
|
|
1187
|
+
receipt = new TxReceipt(
|
|
1188
|
+
txHash,
|
|
1189
|
+
TxStatus.DROPPED,
|
|
1190
|
+
/*executionResult=*/ undefined,
|
|
1191
|
+
/*error=*/ 'Tx dropped by P2P node',
|
|
1192
|
+
);
|
|
955
1193
|
}
|
|
956
1194
|
|
|
957
1195
|
this.debugLogStore.decorateReceiptWithLogs(txHash.toString(), receipt);
|
|
@@ -968,6 +1206,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
968
1206
|
*/
|
|
969
1207
|
public async stop() {
|
|
970
1208
|
this.log.info(`Stopping Aztec Node`);
|
|
1209
|
+
await tryStop(this.attestationsBlockWatcher);
|
|
971
1210
|
await tryStop(this.validatorsSentinel);
|
|
972
1211
|
await tryStop(this.epochPruneWatcher);
|
|
973
1212
|
await tryStop(this.slasherClient);
|
|
@@ -1239,41 +1478,6 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
1239
1478
|
return preimage.leaf.value;
|
|
1240
1479
|
}
|
|
1241
1480
|
|
|
1242
|
-
public async getBlockHeader(block: BlockParameter = 'latest'): Promise<BlockHeader | undefined> {
|
|
1243
|
-
if (BlockHash.isBlockHash(block)) {
|
|
1244
|
-
const initialBlockHash = await this.#getInitialHeaderHash();
|
|
1245
|
-
if (block.equals(initialBlockHash)) {
|
|
1246
|
-
// Block source doesn't handle initial header so we need to handle the case separately.
|
|
1247
|
-
return this.worldStateSynchronizer.getCommitted().getInitialHeader();
|
|
1248
|
-
}
|
|
1249
|
-
return this.blockSource.getBlockHeaderByHash(block);
|
|
1250
|
-
} else {
|
|
1251
|
-
// Block source doesn't handle initial header so we need to handle the case separately.
|
|
1252
|
-
const blockNumber = block === 'latest' ? await this.getBlockNumber() : (block as BlockNumber);
|
|
1253
|
-
if (blockNumber === BlockNumber.ZERO) {
|
|
1254
|
-
return this.worldStateSynchronizer.getCommitted().getInitialHeader();
|
|
1255
|
-
}
|
|
1256
|
-
return this.blockSource.getBlockHeader(block);
|
|
1257
|
-
}
|
|
1258
|
-
}
|
|
1259
|
-
|
|
1260
|
-
/**
|
|
1261
|
-
* Get a block header specified by its archive root.
|
|
1262
|
-
* @param archive - The archive root being requested.
|
|
1263
|
-
* @returns The requested block header.
|
|
1264
|
-
*/
|
|
1265
|
-
public async getBlockHeaderByArchive(archive: Fr): Promise<BlockHeader | undefined> {
|
|
1266
|
-
return await this.blockSource.getBlockHeaderByArchive(archive);
|
|
1267
|
-
}
|
|
1268
|
-
|
|
1269
|
-
public getBlockData(number: BlockNumber): Promise<BlockData | undefined> {
|
|
1270
|
-
return this.blockSource.getBlockData(number);
|
|
1271
|
-
}
|
|
1272
|
-
|
|
1273
|
-
public getBlockDataByArchive(archive: Fr): Promise<BlockData | undefined> {
|
|
1274
|
-
return this.blockSource.getBlockDataByArchive(archive);
|
|
1275
|
-
}
|
|
1276
|
-
|
|
1277
1481
|
/**
|
|
1278
1482
|
* Simulates the public part of a transaction with the current state.
|
|
1279
1483
|
* @param tx - The transaction to simulate.
|
|
@@ -1714,25 +1918,28 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
1714
1918
|
if (BlockHash.isBlockHash(block)) {
|
|
1715
1919
|
const initialBlockHash = await this.#getInitialHeaderHash();
|
|
1716
1920
|
if (block.equals(initialBlockHash)) {
|
|
1717
|
-
// Block
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1921
|
+
// Block 0 is a first-class historical block: its state lives in the trees' persisted
|
|
1922
|
+
// block-0 payload. Resolving the genesis hash to block number 0 lets the snapshot path
|
|
1923
|
+
// pin reads to genesis state even after the node has advanced past it.
|
|
1924
|
+
blockNumber = BlockNumber.ZERO;
|
|
1925
|
+
} else {
|
|
1926
|
+
const header = await this.blockSource.getBlockHeaderByHash(block);
|
|
1927
|
+
if (!header) {
|
|
1928
|
+
throw new Error(
|
|
1929
|
+
`Block hash ${block.toString()} not found when querying world state. If the node API has been queried with anchor block hash possibly a reorg has occurred.`,
|
|
1930
|
+
);
|
|
1931
|
+
}
|
|
1932
|
+
blockNumber = header.getBlockNumber();
|
|
1726
1933
|
}
|
|
1727
|
-
|
|
1728
|
-
blockNumber = header.getBlockNumber();
|
|
1729
1934
|
} else {
|
|
1730
1935
|
blockNumber = block as BlockNumber;
|
|
1731
1936
|
}
|
|
1732
1937
|
|
|
1733
1938
|
// Check it's within world state sync range
|
|
1734
1939
|
if (blockNumber > blockSyncedTo) {
|
|
1735
|
-
throw new Error(
|
|
1940
|
+
throw new Error(
|
|
1941
|
+
`Queried block ${inspectBlockParameter(block)} not yet synced by the node (node is synced upto ${blockSyncedTo}).`,
|
|
1942
|
+
);
|
|
1736
1943
|
}
|
|
1737
1944
|
this.log.debug(`Using snapshot for block ${blockNumber}, world state synced upto ${blockSyncedTo}`);
|
|
1738
1945
|
|
|
@@ -1742,8 +1949,9 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
1742
1949
|
if (BlockHash.isBlockHash(block)) {
|
|
1743
1950
|
const blockHash = await snapshot.getLeafValue(MerkleTreeId.ARCHIVE, BigInt(blockNumber));
|
|
1744
1951
|
if (!blockHash || !block.equals(blockHash)) {
|
|
1952
|
+
const initialBlockHash = await this.#getInitialHeaderHash();
|
|
1745
1953
|
throw new Error(
|
|
1746
|
-
`Block hash ${block.toString()} not found in world state at block number ${blockNumber}. If the node API has been queried with anchor block hash possibly a reorg has occurred.`,
|
|
1954
|
+
`Block hash ${block.toString()} not found in world state at block number ${blockNumber} (world state has ${blockHash?.toString() ?? 'no hash'} at that index, genesis header hash is ${initialBlockHash.toString()}). If the node API has been queried with anchor block hash possibly a reorg has occurred.`,
|
|
1747
1955
|
);
|
|
1748
1956
|
}
|
|
1749
1957
|
}
|
|
@@ -1751,23 +1959,31 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
1751
1959
|
return snapshot;
|
|
1752
1960
|
}
|
|
1753
1961
|
|
|
1754
|
-
/** Resolves
|
|
1962
|
+
/** Resolves any {@link BlockParameter} variant to a concrete block number. */
|
|
1755
1963
|
protected async resolveBlockNumber(block: BlockParameter): Promise<BlockNumber> {
|
|
1756
|
-
|
|
1757
|
-
|
|
1964
|
+
const resolved = await this.resolveBlockParameter(block);
|
|
1965
|
+
if (resolved.number !== undefined) {
|
|
1966
|
+
return resolved.number;
|
|
1758
1967
|
}
|
|
1759
|
-
if (
|
|
1968
|
+
if (resolved.hash !== undefined) {
|
|
1760
1969
|
const initialBlockHash = await this.#getInitialHeaderHash();
|
|
1761
|
-
if (
|
|
1970
|
+
if (resolved.hash.equals(initialBlockHash)) {
|
|
1762
1971
|
return BlockNumber.ZERO;
|
|
1763
1972
|
}
|
|
1764
|
-
const header = await this.blockSource.getBlockHeaderByHash(
|
|
1973
|
+
const header = await this.blockSource.getBlockHeaderByHash(resolved.hash);
|
|
1974
|
+
if (!header) {
|
|
1975
|
+
throw new Error(`Block hash ${resolved.hash.toString()} not found.`);
|
|
1976
|
+
}
|
|
1977
|
+
return header.getBlockNumber();
|
|
1978
|
+
}
|
|
1979
|
+
if (resolved.archive !== undefined) {
|
|
1980
|
+
const header = await this.blockSource.getBlockHeaderByArchive(resolved.archive);
|
|
1765
1981
|
if (!header) {
|
|
1766
|
-
throw new Error(`Block
|
|
1982
|
+
throw new Error(`Block with archive ${resolved.archive.toString()} not found.`);
|
|
1767
1983
|
}
|
|
1768
1984
|
return header.getBlockNumber();
|
|
1769
1985
|
}
|
|
1770
|
-
|
|
1986
|
+
throw new BadRequestError(`Invalid BlockParameter: ${JSON.stringify(block)}`);
|
|
1771
1987
|
}
|
|
1772
1988
|
|
|
1773
1989
|
/**
|