@aztec/aztec-node 0.0.1-commit.0dc957cde → 0.0.1-commit.0ec55a70b
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 +312 -117
- 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 +342 -146
- 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.
|
|
@@ -403,7 +687,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
403
687
|
|
|
404
688
|
let validatorClient: ValidatorClient | undefined;
|
|
405
689
|
|
|
406
|
-
if (!
|
|
690
|
+
if (!config.disableValidator) {
|
|
407
691
|
// Create validator client if required
|
|
408
692
|
validatorClient = await createValidatorClient(config, {
|
|
409
693
|
checkpointsBuilder: validatorCheckpointsBuilder,
|
|
@@ -494,9 +778,18 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
494
778
|
void archiver
|
|
495
779
|
.waitForInitialSync()
|
|
496
780
|
.then(async () => {
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
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
|
+
}
|
|
500
793
|
log.info(`All p2p services started`);
|
|
501
794
|
})
|
|
502
795
|
.catch(err => log.error('Failed to start p2p services after archiver sync', err));
|
|
@@ -627,6 +920,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
627
920
|
slasherClient,
|
|
628
921
|
validatorsSentinel,
|
|
629
922
|
epochPruneWatcher,
|
|
923
|
+
attestationsBlockWatcher,
|
|
630
924
|
ethereumChain.chainInfo.id,
|
|
631
925
|
config.rollupVersion,
|
|
632
926
|
globalVariableBuilder,
|
|
@@ -725,71 +1019,6 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
725
1019
|
return nodeInfo;
|
|
726
1020
|
}
|
|
727
1021
|
|
|
728
|
-
/**
|
|
729
|
-
* Get a block specified by its block number, block hash, or 'latest'.
|
|
730
|
-
* @param block - The block parameter (block number, block hash, or 'latest').
|
|
731
|
-
* @returns The requested block.
|
|
732
|
-
*/
|
|
733
|
-
public async getBlock(block: BlockParameter): Promise<L2Block | undefined> {
|
|
734
|
-
if (BlockHash.isBlockHash(block)) {
|
|
735
|
-
return this.getBlockByHash(block);
|
|
736
|
-
}
|
|
737
|
-
const blockNumber = block === 'latest' ? await this.getBlockNumber() : (block as BlockNumber);
|
|
738
|
-
if (blockNumber === BlockNumber.ZERO) {
|
|
739
|
-
return this.buildInitialBlock();
|
|
740
|
-
}
|
|
741
|
-
return await this.blockSource.getL2Block(blockNumber);
|
|
742
|
-
}
|
|
743
|
-
|
|
744
|
-
/**
|
|
745
|
-
* Get a block specified by its hash.
|
|
746
|
-
* @param blockHash - The block hash being requested.
|
|
747
|
-
* @returns The requested block.
|
|
748
|
-
*/
|
|
749
|
-
public async getBlockByHash(blockHash: BlockHash): Promise<L2Block | undefined> {
|
|
750
|
-
const initialBlockHash = await this.#getInitialHeaderHash();
|
|
751
|
-
if (blockHash.equals(initialBlockHash)) {
|
|
752
|
-
return this.buildInitialBlock();
|
|
753
|
-
}
|
|
754
|
-
return await this.blockSource.getL2BlockByHash(blockHash);
|
|
755
|
-
}
|
|
756
|
-
|
|
757
|
-
private buildInitialBlock(): L2Block {
|
|
758
|
-
const initialHeader = this.worldStateSynchronizer.getCommitted().getInitialHeader();
|
|
759
|
-
return L2Block.empty(initialHeader);
|
|
760
|
-
}
|
|
761
|
-
|
|
762
|
-
/**
|
|
763
|
-
* Get a block specified by its archive root.
|
|
764
|
-
* @param archive - The archive root being requested.
|
|
765
|
-
* @returns The requested block.
|
|
766
|
-
*/
|
|
767
|
-
public async getBlockByArchive(archive: Fr): Promise<L2Block | undefined> {
|
|
768
|
-
return await this.blockSource.getL2BlockByArchive(archive);
|
|
769
|
-
}
|
|
770
|
-
|
|
771
|
-
/**
|
|
772
|
-
* Method to request blocks. Will attempt to return all requested blocks but will return only those available.
|
|
773
|
-
* @param from - The start of the range of blocks to return.
|
|
774
|
-
* @param limit - The maximum number of blocks to obtain.
|
|
775
|
-
* @returns The blocks requested.
|
|
776
|
-
*/
|
|
777
|
-
public async getBlocks(from: BlockNumber, limit: number): Promise<L2Block[]> {
|
|
778
|
-
return (await this.blockSource.getBlocks(from, BlockNumber(limit))) ?? [];
|
|
779
|
-
}
|
|
780
|
-
|
|
781
|
-
public async getCheckpoints(from: CheckpointNumber, limit: number): Promise<PublishedCheckpoint[]> {
|
|
782
|
-
return (await this.blockSource.getCheckpoints(from, limit)) ?? [];
|
|
783
|
-
}
|
|
784
|
-
|
|
785
|
-
public async getCheckpointedBlocks(from: BlockNumber, limit: number) {
|
|
786
|
-
return (await this.blockSource.getCheckpointedBlocks(from, limit)) ?? [];
|
|
787
|
-
}
|
|
788
|
-
|
|
789
|
-
public getCheckpointsDataForEpoch(epochNumber: EpochNumber) {
|
|
790
|
-
return this.blockSource.getCheckpointsDataForEpoch(epochNumber);
|
|
791
|
-
}
|
|
792
|
-
|
|
793
1022
|
public async getCurrentMinFees(): Promise<GasFees> {
|
|
794
1023
|
return await this.feeProvider.getCurrentMinFees();
|
|
795
1024
|
}
|
|
@@ -807,26 +1036,6 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
807
1036
|
return GasFees.from({ feePerDaGas: 0n, feePerL2Gas: 0n });
|
|
808
1037
|
}
|
|
809
1038
|
|
|
810
|
-
/**
|
|
811
|
-
* Method to fetch the latest block number synchronized by the node.
|
|
812
|
-
* @returns The block number.
|
|
813
|
-
*/
|
|
814
|
-
public async getBlockNumber(): Promise<BlockNumber> {
|
|
815
|
-
return await this.blockSource.getBlockNumber();
|
|
816
|
-
}
|
|
817
|
-
|
|
818
|
-
public async getProvenBlockNumber(): Promise<BlockNumber> {
|
|
819
|
-
return await this.blockSource.getProvenBlockNumber();
|
|
820
|
-
}
|
|
821
|
-
|
|
822
|
-
public async getCheckpointedBlockNumber(): Promise<BlockNumber> {
|
|
823
|
-
return await this.blockSource.getCheckpointedL2BlockNumber();
|
|
824
|
-
}
|
|
825
|
-
|
|
826
|
-
public getCheckpointNumber(): Promise<CheckpointNumber> {
|
|
827
|
-
return this.blockSource.getCheckpointNumber();
|
|
828
|
-
}
|
|
829
|
-
|
|
830
1039
|
/**
|
|
831
1040
|
* Method to fetch the version of the package.
|
|
832
1041
|
* @returns The node package version
|
|
@@ -944,7 +1153,13 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
944
1153
|
throw new Error(`Invalid tx: ${reason}`);
|
|
945
1154
|
}
|
|
946
1155
|
|
|
947
|
-
|
|
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
|
+
}
|
|
948
1163
|
const duration = timer.ms();
|
|
949
1164
|
this.metrics.receivedTx(duration, true);
|
|
950
1165
|
this.log.info(`Received tx ${txHash} in ${duration}ms`, { txHash });
|
|
@@ -966,10 +1181,15 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
966
1181
|
// If the tx is in the pool but not in the archiver, it's pending.
|
|
967
1182
|
// This handles race conditions between archiver and p2p, where the archiver
|
|
968
1183
|
// has pruned the block in which a tx was mined, but p2p has not caught up yet.
|
|
969
|
-
receipt = new TxReceipt(txHash, TxStatus.PENDING, undefined, undefined);
|
|
1184
|
+
receipt = new TxReceipt(txHash, TxStatus.PENDING, /*executionResult=*/ undefined, /*error=*/ undefined);
|
|
970
1185
|
} else {
|
|
971
1186
|
// Otherwise, if we don't know the tx, we consider it dropped.
|
|
972
|
-
receipt = new TxReceipt(
|
|
1187
|
+
receipt = new TxReceipt(
|
|
1188
|
+
txHash,
|
|
1189
|
+
TxStatus.DROPPED,
|
|
1190
|
+
/*executionResult=*/ undefined,
|
|
1191
|
+
/*error=*/ 'Tx dropped by P2P node',
|
|
1192
|
+
);
|
|
973
1193
|
}
|
|
974
1194
|
|
|
975
1195
|
this.debugLogStore.decorateReceiptWithLogs(txHash.toString(), receipt);
|
|
@@ -986,6 +1206,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
986
1206
|
*/
|
|
987
1207
|
public async stop() {
|
|
988
1208
|
this.log.info(`Stopping Aztec Node`);
|
|
1209
|
+
await tryStop(this.attestationsBlockWatcher);
|
|
989
1210
|
await tryStop(this.validatorsSentinel);
|
|
990
1211
|
await tryStop(this.epochPruneWatcher);
|
|
991
1212
|
await tryStop(this.slasherClient);
|
|
@@ -1257,41 +1478,6 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
1257
1478
|
return preimage.leaf.value;
|
|
1258
1479
|
}
|
|
1259
1480
|
|
|
1260
|
-
public async getBlockHeader(block: BlockParameter = 'latest'): Promise<BlockHeader | undefined> {
|
|
1261
|
-
if (BlockHash.isBlockHash(block)) {
|
|
1262
|
-
const initialBlockHash = await this.#getInitialHeaderHash();
|
|
1263
|
-
if (block.equals(initialBlockHash)) {
|
|
1264
|
-
// Block source doesn't handle initial header so we need to handle the case separately.
|
|
1265
|
-
return this.worldStateSynchronizer.getCommitted().getInitialHeader();
|
|
1266
|
-
}
|
|
1267
|
-
return this.blockSource.getBlockHeaderByHash(block);
|
|
1268
|
-
} else {
|
|
1269
|
-
// Block source doesn't handle initial header so we need to handle the case separately.
|
|
1270
|
-
const blockNumber = block === 'latest' ? await this.getBlockNumber() : (block as BlockNumber);
|
|
1271
|
-
if (blockNumber === BlockNumber.ZERO) {
|
|
1272
|
-
return this.worldStateSynchronizer.getCommitted().getInitialHeader();
|
|
1273
|
-
}
|
|
1274
|
-
return this.blockSource.getBlockHeader(block);
|
|
1275
|
-
}
|
|
1276
|
-
}
|
|
1277
|
-
|
|
1278
|
-
/**
|
|
1279
|
-
* Get a block header specified by its archive root.
|
|
1280
|
-
* @param archive - The archive root being requested.
|
|
1281
|
-
* @returns The requested block header.
|
|
1282
|
-
*/
|
|
1283
|
-
public async getBlockHeaderByArchive(archive: Fr): Promise<BlockHeader | undefined> {
|
|
1284
|
-
return await this.blockSource.getBlockHeaderByArchive(archive);
|
|
1285
|
-
}
|
|
1286
|
-
|
|
1287
|
-
public getBlockData(number: BlockNumber): Promise<BlockData | undefined> {
|
|
1288
|
-
return this.blockSource.getBlockData(number);
|
|
1289
|
-
}
|
|
1290
|
-
|
|
1291
|
-
public getBlockDataByArchive(archive: Fr): Promise<BlockData | undefined> {
|
|
1292
|
-
return this.blockSource.getBlockDataByArchive(archive);
|
|
1293
|
-
}
|
|
1294
|
-
|
|
1295
1481
|
/**
|
|
1296
1482
|
* Simulates the public part of a transaction with the current state.
|
|
1297
1483
|
* @param tx - The transaction to simulate.
|
|
@@ -1751,7 +1937,9 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
1751
1937
|
|
|
1752
1938
|
// Check it's within world state sync range
|
|
1753
1939
|
if (blockNumber > blockSyncedTo) {
|
|
1754
|
-
throw new Error(
|
|
1940
|
+
throw new Error(
|
|
1941
|
+
`Queried block ${inspectBlockParameter(block)} not yet synced by the node (node is synced upto ${blockSyncedTo}).`,
|
|
1942
|
+
);
|
|
1755
1943
|
}
|
|
1756
1944
|
this.log.debug(`Using snapshot for block ${blockNumber}, world state synced upto ${blockSyncedTo}`);
|
|
1757
1945
|
|
|
@@ -1771,23 +1959,31 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
1771
1959
|
return snapshot;
|
|
1772
1960
|
}
|
|
1773
1961
|
|
|
1774
|
-
/** Resolves
|
|
1962
|
+
/** Resolves any {@link BlockParameter} variant to a concrete block number. */
|
|
1775
1963
|
protected async resolveBlockNumber(block: BlockParameter): Promise<BlockNumber> {
|
|
1776
|
-
|
|
1777
|
-
|
|
1964
|
+
const resolved = await this.resolveBlockParameter(block);
|
|
1965
|
+
if (resolved.number !== undefined) {
|
|
1966
|
+
return resolved.number;
|
|
1778
1967
|
}
|
|
1779
|
-
if (
|
|
1968
|
+
if (resolved.hash !== undefined) {
|
|
1780
1969
|
const initialBlockHash = await this.#getInitialHeaderHash();
|
|
1781
|
-
if (
|
|
1970
|
+
if (resolved.hash.equals(initialBlockHash)) {
|
|
1782
1971
|
return BlockNumber.ZERO;
|
|
1783
1972
|
}
|
|
1784
|
-
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);
|
|
1785
1981
|
if (!header) {
|
|
1786
|
-
throw new Error(`Block
|
|
1982
|
+
throw new Error(`Block with archive ${resolved.archive.toString()} not found.`);
|
|
1787
1983
|
}
|
|
1788
1984
|
return header.getBlockNumber();
|
|
1789
1985
|
}
|
|
1790
|
-
|
|
1986
|
+
throw new BadRequestError(`Invalid BlockParameter: ${JSON.stringify(block)}`);
|
|
1791
1987
|
}
|
|
1792
1988
|
|
|
1793
1989
|
/**
|
package/src/sentinel/factory.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { createLogger } from '@aztec/foundation/log';
|
|
|
3
3
|
import { createStore } from '@aztec/kv-store/lmdb-v2';
|
|
4
4
|
import type { P2PClient } from '@aztec/p2p';
|
|
5
5
|
import type { L2BlockSource } from '@aztec/stdlib/block';
|
|
6
|
+
import type { ChainConfig } from '@aztec/stdlib/config';
|
|
6
7
|
import type { SlasherConfig } from '@aztec/stdlib/interfaces/server';
|
|
7
8
|
import type { DataStoreConfig } from '@aztec/stdlib/kv-store';
|
|
8
9
|
|
|
@@ -14,7 +15,7 @@ export async function createSentinel(
|
|
|
14
15
|
epochCache: EpochCache,
|
|
15
16
|
archiver: L2BlockSource,
|
|
16
17
|
p2p: P2PClient,
|
|
17
|
-
config: SentinelConfig & DataStoreConfig & SlasherConfig,
|
|
18
|
+
config: SentinelConfig & DataStoreConfig & SlasherConfig & Pick<ChainConfig, 'l1ChainId' | 'l1Contracts'>,
|
|
18
19
|
logger = createLogger('node:sentinel'),
|
|
19
20
|
): Promise<Sentinel | undefined> {
|
|
20
21
|
if (!config.sentinelEnabled) {
|
package/src/sentinel/sentinel.ts
CHANGED
|
@@ -21,7 +21,9 @@ import {
|
|
|
21
21
|
type L2BlockStreamEventHandler,
|
|
22
22
|
getAttestationInfoFromPublishedCheckpoint,
|
|
23
23
|
} from '@aztec/stdlib/block';
|
|
24
|
+
import type { ChainConfig } from '@aztec/stdlib/config';
|
|
24
25
|
import { getEpochAtSlot, getSlotRangeForEpoch, getTimestampForSlot } from '@aztec/stdlib/epoch-helpers';
|
|
26
|
+
import type { CoordinationSignatureContext } from '@aztec/stdlib/p2p';
|
|
25
27
|
import type {
|
|
26
28
|
SingleValidatorStats,
|
|
27
29
|
ValidatorStats,
|
|
@@ -36,6 +38,12 @@ import EventEmitter from 'node:events';
|
|
|
36
38
|
|
|
37
39
|
import { SentinelStore } from './store.js';
|
|
38
40
|
|
|
41
|
+
export type SentinelRuntimeConfig = Pick<
|
|
42
|
+
SlasherConfig,
|
|
43
|
+
'slashInactivityTargetPercentage' | 'slashInactivityPenalty' | 'slashInactivityConsecutiveEpochThreshold'
|
|
44
|
+
> &
|
|
45
|
+
Pick<ChainConfig, 'l1ChainId' | 'l1Contracts'>;
|
|
46
|
+
|
|
39
47
|
/** Maps a validator status to its category: proposer or attestation. */
|
|
40
48
|
function statusToCategory(status: ValidatorStatusInSlot): ValidatorStatusType {
|
|
41
49
|
switch (status) {
|
|
@@ -65,10 +73,7 @@ export class Sentinel extends (EventEmitter as new () => WatcherEmitter) impleme
|
|
|
65
73
|
protected archiver: L2BlockSource,
|
|
66
74
|
protected p2p: P2PClient,
|
|
67
75
|
protected store: SentinelStore,
|
|
68
|
-
protected config:
|
|
69
|
-
SlasherConfig,
|
|
70
|
-
'slashInactivityTargetPercentage' | 'slashInactivityPenalty' | 'slashInactivityConsecutiveEpochThreshold'
|
|
71
|
-
>,
|
|
76
|
+
protected config: SentinelRuntimeConfig,
|
|
72
77
|
protected logger = createLogger('node:sentinel'),
|
|
73
78
|
) {
|
|
74
79
|
super();
|
|
@@ -77,6 +82,13 @@ export class Sentinel extends (EventEmitter as new () => WatcherEmitter) impleme
|
|
|
77
82
|
this.runningPromise = new RunningPromise(this.work.bind(this), logger, interval);
|
|
78
83
|
}
|
|
79
84
|
|
|
85
|
+
private getSignatureContext(): CoordinationSignatureContext {
|
|
86
|
+
return {
|
|
87
|
+
chainId: this.config.l1ChainId,
|
|
88
|
+
rollupAddress: this.config.l1Contracts.rollupAddress,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
80
92
|
public updateConfig(config: Partial<SlasherConfig>) {
|
|
81
93
|
this.config = { ...this.config, ...config };
|
|
82
94
|
}
|
|
@@ -117,7 +129,7 @@ export class Sentinel extends (EventEmitter as new () => WatcherEmitter) impleme
|
|
|
117
129
|
this.slotNumberToCheckpoint.set(checkpoint.checkpoint.header.slotNumber, {
|
|
118
130
|
checkpointNumber: checkpoint.checkpoint.number,
|
|
119
131
|
archive: checkpoint.checkpoint.archive.root.toString(),
|
|
120
|
-
attestors: getAttestationInfoFromPublishedCheckpoint(checkpoint)
|
|
132
|
+
attestors: getAttestationInfoFromPublishedCheckpoint(checkpoint, this.getSignatureContext())
|
|
121
133
|
.filter(a => a.status === 'recovered-from-signature')
|
|
122
134
|
.map(a => a.address!),
|
|
123
135
|
});
|