@aztec/aztec-node 0.0.1-commit.e588bc7e5 → 0.0.1-commit.e5a3663dd
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 +32 -65
- package/dest/aztec-node/server.d.ts.map +1 -1
- package/dest/aztec-node/server.js +600 -355
- 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 +8 -2
- package/package.json +26 -26
- package/src/aztec-node/block_response_helpers.ts +131 -0
- package/src/aztec-node/server.ts +717 -461
- package/src/sentinel/factory.ts +2 -1
- package/src/sentinel/sentinel.ts +18 -6
package/src/aztec-node/server.ts
CHANGED
|
@@ -16,6 +16,7 @@ import { Fr } from '@aztec/foundation/curves/bn254';
|
|
|
16
16
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
17
17
|
import { BadRequestError } from '@aztec/foundation/json-rpc';
|
|
18
18
|
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
19
|
+
import { retryUntil } from '@aztec/foundation/retry';
|
|
19
20
|
import { count } from '@aztec/foundation/string';
|
|
20
21
|
import { DateProvider, Timer } from '@aztec/foundation/timer';
|
|
21
22
|
import { MembershipWitness, SiblingPath } from '@aztec/foundation/trees';
|
|
@@ -32,7 +33,12 @@ import {
|
|
|
32
33
|
import { ProtocolContractAddress } from '@aztec/protocol-contracts';
|
|
33
34
|
import { type ProverNode, type ProverNodeDeps, createProverNode } from '@aztec/prover-node';
|
|
34
35
|
import { createKeyStoreForProver } from '@aztec/prover-node/config';
|
|
35
|
-
import {
|
|
36
|
+
import {
|
|
37
|
+
FeeProviderImpl,
|
|
38
|
+
GlobalVariableBuilder,
|
|
39
|
+
SequencerClient,
|
|
40
|
+
type SequencerPublisher,
|
|
41
|
+
} from '@aztec/sequencer-client';
|
|
36
42
|
import { PublicProcessorFactory } from '@aztec/simulator/server';
|
|
37
43
|
import {
|
|
38
44
|
AttestationsBlockWatcher,
|
|
@@ -44,14 +50,13 @@ import {
|
|
|
44
50
|
import { CollectionLimitsConfig, PublicSimulatorConfig } from '@aztec/stdlib/avm';
|
|
45
51
|
import { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
46
52
|
import {
|
|
47
|
-
type BlockData,
|
|
48
53
|
BlockHash,
|
|
49
54
|
type BlockParameter,
|
|
50
55
|
type DataInBlock,
|
|
51
56
|
L2Block,
|
|
52
57
|
type L2BlockSource,
|
|
58
|
+
inspectBlockParameter,
|
|
53
59
|
} from '@aztec/stdlib/block';
|
|
54
|
-
import type { PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
|
|
55
60
|
import type {
|
|
56
61
|
ContractClassPublic,
|
|
57
62
|
ContractDataSource,
|
|
@@ -59,16 +64,24 @@ import type {
|
|
|
59
64
|
NodeInfo,
|
|
60
65
|
ProtocolContractAddresses,
|
|
61
66
|
} from '@aztec/stdlib/contract';
|
|
62
|
-
import { GasFees } from '@aztec/stdlib/gas';
|
|
67
|
+
import { GasFees, type ManaUsageEstimate } from '@aztec/stdlib/gas';
|
|
63
68
|
import { computePublicDataTreeLeafSlot } from '@aztec/stdlib/hash';
|
|
64
|
-
import {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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,
|
|
71
83
|
} from '@aztec/stdlib/interfaces/client';
|
|
84
|
+
import { AztecNodeAdminConfigSchema } from '@aztec/stdlib/interfaces/client';
|
|
72
85
|
import {
|
|
73
86
|
type AllowedElement,
|
|
74
87
|
type ClientProtocolCircuitVerifier,
|
|
@@ -81,11 +94,12 @@ import {
|
|
|
81
94
|
import type { DebugLogStore, LogFilter, SiloedTag, Tag, TxScopedL2Log } from '@aztec/stdlib/logs';
|
|
82
95
|
import { InMemoryDebugLogStore, NullDebugLogStore } from '@aztec/stdlib/logs';
|
|
83
96
|
import { InboxLeaf, type L1ToL2MessageSource } from '@aztec/stdlib/messaging';
|
|
84
|
-
import type { Offense
|
|
85
|
-
import type { NullifierLeafPreimage,
|
|
97
|
+
import type { Offense } from '@aztec/stdlib/slashing';
|
|
98
|
+
import type { NullifierLeafPreimage, PublicDataTreeLeafPreimage } from '@aztec/stdlib/trees';
|
|
86
99
|
import { MerkleTreeId, NullifierMembershipWitness, PublicDataWitness } from '@aztec/stdlib/trees';
|
|
87
100
|
import {
|
|
88
101
|
type BlockHeader,
|
|
102
|
+
type FeeProvider,
|
|
89
103
|
type GlobalVariableBuilder as GlobalVariableBuilderInterface,
|
|
90
104
|
type IndexedTxEffect,
|
|
91
105
|
PublicSimulationOutput,
|
|
@@ -97,6 +111,7 @@ import {
|
|
|
97
111
|
} from '@aztec/stdlib/tx';
|
|
98
112
|
import { getPackageVersion } from '@aztec/stdlib/update-checker';
|
|
99
113
|
import type { SingleValidatorStats, ValidatorsStats } from '@aztec/stdlib/validators';
|
|
114
|
+
import type { GenesisData } from '@aztec/stdlib/world-state';
|
|
100
115
|
import {
|
|
101
116
|
Attributes,
|
|
102
117
|
type TelemetryClient,
|
|
@@ -120,13 +135,19 @@ import { createPublicClient } from 'viem';
|
|
|
120
135
|
|
|
121
136
|
import { createSentinel } from '../sentinel/factory.js';
|
|
122
137
|
import { Sentinel } from '../sentinel/sentinel.js';
|
|
138
|
+
import {
|
|
139
|
+
blockResponseFromBlockData,
|
|
140
|
+
blockResponseFromL2Block,
|
|
141
|
+
checkpointResponseFromCheckpointData,
|
|
142
|
+
checkpointResponseFromPublishedCheckpoint,
|
|
143
|
+
} from './block_response_helpers.js';
|
|
123
144
|
import { type AztecNodeConfig, createKeyStoreForValidator } from './config.js';
|
|
124
145
|
import { NodeMetrics } from './node_metrics.js';
|
|
125
146
|
|
|
126
147
|
/**
|
|
127
148
|
* The aztec node.
|
|
128
149
|
*/
|
|
129
|
-
export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
150
|
+
export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDebug, Traceable {
|
|
130
151
|
private metrics: NodeMetrics;
|
|
131
152
|
private initialHeaderHashPromise: Promise<BlockHash> | undefined = undefined;
|
|
132
153
|
|
|
@@ -148,9 +169,11 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
148
169
|
protected readonly slasherClient: SlasherClientInterface | undefined,
|
|
149
170
|
protected readonly validatorsSentinel: Sentinel | undefined,
|
|
150
171
|
protected readonly epochPruneWatcher: EpochPruneWatcher | undefined,
|
|
172
|
+
protected readonly attestationsBlockWatcher: AttestationsBlockWatcher | undefined,
|
|
151
173
|
protected readonly l1ChainId: number,
|
|
152
174
|
protected readonly version: number,
|
|
153
175
|
protected readonly globalVariableBuilder: GlobalVariableBuilderInterface,
|
|
176
|
+
protected readonly feeProvider: FeeProvider,
|
|
154
177
|
protected readonly epochCache: EpochCacheInterface,
|
|
155
178
|
protected readonly packageVersion: string,
|
|
156
179
|
private peerProofVerifier: ClientProtocolCircuitVerifier,
|
|
@@ -186,10 +209,281 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
186
209
|
return status.syncSummary;
|
|
187
210
|
}
|
|
188
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
|
+
|
|
189
217
|
public getL2Tips() {
|
|
190
218
|
return this.blockSource.getL2Tips();
|
|
191
219
|
}
|
|
192
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
|
+
|
|
193
487
|
/**
|
|
194
488
|
* initializes the Aztec Node, wait for component to sync.
|
|
195
489
|
* @param config - The configuration to be used by the aztec node.
|
|
@@ -207,7 +501,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
207
501
|
slashingProtectionDb?: SlashingProtectionDatabase;
|
|
208
502
|
} = {},
|
|
209
503
|
options: {
|
|
210
|
-
|
|
504
|
+
genesis?: GenesisData;
|
|
211
505
|
dontStartSequencer?: boolean;
|
|
212
506
|
dontStartProverNode?: boolean;
|
|
213
507
|
} = {},
|
|
@@ -303,328 +597,354 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
303
597
|
|
|
304
598
|
const epochCache = await EpochCache.create(config.l1Contracts.rollupAddress, config, { dateProvider });
|
|
305
599
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
const worldStateSynchronizer = await createWorldStateSynchronizer(
|
|
314
|
-
config,
|
|
315
|
-
archiver,
|
|
316
|
-
options.prefilledPublicData,
|
|
317
|
-
telemetry,
|
|
318
|
-
);
|
|
319
|
-
const useRealVerifiers = config.realProofs || config.debugForceTxProofVerification;
|
|
320
|
-
let peerProofVerifier: ClientProtocolCircuitVerifier;
|
|
321
|
-
let rpcProofVerifier: ClientProtocolCircuitVerifier;
|
|
322
|
-
if (useRealVerifiers) {
|
|
323
|
-
peerProofVerifier = await BatchChonkVerifier.new(config, config.bbChonkVerifyMaxBatch, 'peer');
|
|
324
|
-
const rpcVerifier = await BBCircuitVerifier.new(config);
|
|
325
|
-
rpcProofVerifier = new QueuedIVCVerifier(rpcVerifier, config.numConcurrentIVCVerifiers);
|
|
326
|
-
} else {
|
|
327
|
-
peerProofVerifier = new TestCircuitVerifier(config.proverTestVerificationDelayMs);
|
|
328
|
-
rpcProofVerifier = new TestCircuitVerifier(config.proverTestVerificationDelayMs);
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
let debugLogStore: DebugLogStore;
|
|
332
|
-
if (!config.realProofs) {
|
|
333
|
-
log.warn(`Aztec node is accepting fake proofs`);
|
|
334
|
-
|
|
335
|
-
debugLogStore = new InMemoryDebugLogStore();
|
|
336
|
-
log.info(
|
|
337
|
-
'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 },
|
|
338
607
|
);
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
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);
|
|
347
625
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
archiver,
|
|
352
|
-
peerProofVerifier,
|
|
353
|
-
worldStateSynchronizer,
|
|
354
|
-
epochCache,
|
|
355
|
-
packageVersion,
|
|
356
|
-
dateProvider,
|
|
357
|
-
telemetry,
|
|
358
|
-
deps.p2pClientDeps,
|
|
359
|
-
);
|
|
626
|
+
let debugLogStore: DebugLogStore;
|
|
627
|
+
if (!config.realProofs) {
|
|
628
|
+
log.warn(`Aztec node is accepting fake proofs`);
|
|
360
629
|
|
|
361
|
-
|
|
362
|
-
|
|
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
|
+
}
|
|
363
637
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
...config,
|
|
638
|
+
const globalVariableBuilderConfig = {
|
|
639
|
+
l1Contracts: config.l1Contracts,
|
|
640
|
+
ethereumSlotDuration: config.ethereumSlotDuration,
|
|
641
|
+
rollupVersion: BigInt(config.rollupVersion),
|
|
369
642
|
l1GenesisTime,
|
|
370
643
|
slotDuration: Number(slotDuration),
|
|
371
|
-
|
|
372
|
-
maxTxsPerCheckpoint: config.validateMaxTxsPerCheckpoint,
|
|
373
|
-
},
|
|
374
|
-
worldStateSynchronizer,
|
|
375
|
-
archiver,
|
|
376
|
-
dateProvider,
|
|
377
|
-
telemetry,
|
|
378
|
-
);
|
|
379
|
-
|
|
380
|
-
let validatorClient: ValidatorClient | undefined;
|
|
381
|
-
|
|
382
|
-
if (!proverOnly) {
|
|
383
|
-
// Create validator client if required
|
|
384
|
-
validatorClient = await createValidatorClient(config, {
|
|
385
|
-
checkpointsBuilder: validatorCheckpointsBuilder,
|
|
386
|
-
worldState: worldStateSynchronizer,
|
|
387
|
-
p2pClient,
|
|
388
|
-
telemetry,
|
|
389
|
-
dateProvider,
|
|
390
|
-
epochCache,
|
|
391
|
-
blockSource: archiver,
|
|
392
|
-
l1ToL2MessageSource: archiver,
|
|
393
|
-
keyStoreManager,
|
|
394
|
-
blobClient,
|
|
395
|
-
slashingProtectionDb: deps.slashingProtectionDb,
|
|
396
|
-
});
|
|
397
|
-
|
|
398
|
-
// If we have a validator client, register it as a source of offenses for the slasher,
|
|
399
|
-
// and have it register callbacks on the p2p client *before* we start it, otherwise messages
|
|
400
|
-
// like attestations or auths will fail.
|
|
401
|
-
if (validatorClient) {
|
|
402
|
-
watchers.push(validatorClient);
|
|
644
|
+
};
|
|
403
645
|
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
validatorClient.getProposalHandler().register(p2pClient, true, archiver, getValidatorAddresses);
|
|
646
|
+
const globalVariableBuilder = new GlobalVariableBuilder(dateProvider, publicClient, globalVariableBuilderConfig);
|
|
647
|
+
const feeProvider = new FeeProviderImpl(dateProvider, publicClient, globalVariableBuilderConfig);
|
|
407
648
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
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');
|
|
411
652
|
}
|
|
412
|
-
}
|
|
413
653
|
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
log.info(`Setting up proposal handler` + (reexecute ? ' with reexecution of proposals' : ''));
|
|
421
|
-
createProposalHandler(config, {
|
|
422
|
-
checkpointsBuilder: validatorCheckpointsBuilder,
|
|
423
|
-
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,
|
|
424
660
|
epochCache,
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
p2pClient,
|
|
428
|
-
blobClient,
|
|
661
|
+
feeProvider,
|
|
662
|
+
packageVersion,
|
|
429
663
|
dateProvider,
|
|
430
664
|
telemetry,
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
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
|
+
);
|
|
439
687
|
|
|
440
|
-
|
|
441
|
-
let epochPruneWatcher: EpochPruneWatcher | undefined;
|
|
442
|
-
let attestationsBlockWatcher: AttestationsBlockWatcher | undefined;
|
|
688
|
+
let validatorClient: ValidatorClient | undefined;
|
|
443
689
|
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
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
|
+
}
|
|
448
720
|
}
|
|
449
721
|
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
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,
|
|
454
732
|
epochCache,
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
733
|
+
blockSource: archiver,
|
|
734
|
+
l1ToL2MessageSource: archiver,
|
|
735
|
+
p2pClient,
|
|
736
|
+
blobClient,
|
|
737
|
+
dateProvider,
|
|
738
|
+
telemetry,
|
|
739
|
+
}).register(p2pClient, reexecute, archiver);
|
|
460
740
|
}
|
|
461
741
|
|
|
462
|
-
//
|
|
463
|
-
|
|
464
|
-
attestationsBlockWatcher = new AttestationsBlockWatcher(archiver, epochCache, config);
|
|
465
|
-
watchers.push(attestationsBlockWatcher);
|
|
466
|
-
}
|
|
467
|
-
}
|
|
742
|
+
// Start world state and wait for it to sync to the archiver.
|
|
743
|
+
await worldStateSynchronizer.start();
|
|
468
744
|
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
.waitForInitialSync()
|
|
472
|
-
.then(async () => {
|
|
473
|
-
await p2pClient.start();
|
|
474
|
-
await validatorsSentinel?.start();
|
|
475
|
-
await epochPruneWatcher?.start();
|
|
476
|
-
await attestationsBlockWatcher?.start();
|
|
477
|
-
log.info(`All p2p services started`);
|
|
478
|
-
})
|
|
479
|
-
.catch(err => log.error('Failed to start p2p services after archiver sync', err));
|
|
480
|
-
|
|
481
|
-
const globalVariableBuilder = new GlobalVariableBuilder(dateProvider, publicClient, {
|
|
482
|
-
l1Contracts: config.l1Contracts,
|
|
483
|
-
ethereumSlotDuration: config.ethereumSlotDuration,
|
|
484
|
-
rollupVersion: BigInt(config.rollupVersion),
|
|
485
|
-
l1GenesisTime,
|
|
486
|
-
slotDuration: Number(slotDuration),
|
|
487
|
-
});
|
|
745
|
+
// Start p2p. Note that it depends on world state to be running.
|
|
746
|
+
await p2pClient.start();
|
|
488
747
|
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
if (!config.disableValidator && validatorClient) {
|
|
493
|
-
// We create a slasher only if we have a sequencer, since all slashing actions go through the sequencer publisher
|
|
494
|
-
// as they are executed when the node is selected as proposer.
|
|
495
|
-
const validatorAddresses = keyStoreManager
|
|
496
|
-
? NodeKeystoreAdapter.fromKeyStoreManager(keyStoreManager).getAddresses()
|
|
497
|
-
: [];
|
|
498
|
-
|
|
499
|
-
slasherClient = await createSlasher(
|
|
500
|
-
config,
|
|
501
|
-
config.l1Contracts,
|
|
502
|
-
getPublicClient(config),
|
|
503
|
-
watchers,
|
|
504
|
-
dateProvider,
|
|
505
|
-
epochCache,
|
|
506
|
-
validatorAddresses,
|
|
507
|
-
undefined, // logger
|
|
508
|
-
);
|
|
509
|
-
await slasherClient.start();
|
|
748
|
+
let validatorsSentinel: Awaited<ReturnType<typeof createSentinel>> | undefined;
|
|
749
|
+
let epochPruneWatcher: EpochPruneWatcher | undefined;
|
|
750
|
+
let attestationsBlockWatcher: AttestationsBlockWatcher | undefined;
|
|
510
751
|
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
752
|
+
if (!proverOnly) {
|
|
753
|
+
validatorsSentinel = await createSentinel(epochCache, archiver, p2pClient, config);
|
|
754
|
+
if (validatorsSentinel && config.slashInactivityPenalty > 0n) {
|
|
755
|
+
watchers.push(validatorsSentinel);
|
|
756
|
+
}
|
|
757
|
+
|
|
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
|
+
}
|
|
769
|
+
|
|
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(
|
|
520
840
|
publicClient,
|
|
521
|
-
|
|
841
|
+
[fundingSigner],
|
|
522
842
|
{ ...config, scope: 'sequencer' },
|
|
523
|
-
{ telemetry, logger: log.createChild('l1-tx-utils'), dateProvider
|
|
843
|
+
{ telemetry, logger: log.createChild('l1-tx-utils:funder'), dateProvider },
|
|
524
844
|
);
|
|
845
|
+
funderL1TxUtils = funder;
|
|
846
|
+
}
|
|
525
847
|
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
{ 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,
|
|
535
856
|
);
|
|
536
|
-
|
|
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
|
+
});
|
|
537
876
|
}
|
|
538
877
|
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
debugLogStore,
|
|
547
|
-
);
|
|
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
|
+
}
|
|
548
885
|
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
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,
|
|
555
912
|
p2pClient,
|
|
913
|
+
archiver,
|
|
914
|
+
archiver,
|
|
915
|
+
archiver,
|
|
916
|
+
archiver,
|
|
556
917
|
worldStateSynchronizer,
|
|
918
|
+
sequencer,
|
|
919
|
+
proverNode,
|
|
557
920
|
slasherClient,
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
blobClient,
|
|
564
|
-
nodeKeyStore: keyStoreManager!,
|
|
921
|
+
validatorsSentinel,
|
|
922
|
+
epochPruneWatcher,
|
|
923
|
+
attestationsBlockWatcher,
|
|
924
|
+
ethereumChain.chainInfo.id,
|
|
925
|
+
config.rollupVersion,
|
|
565
926
|
globalVariableBuilder,
|
|
566
|
-
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
if (!options.dontStartSequencer && sequencer) {
|
|
570
|
-
await sequencer.start();
|
|
571
|
-
log.verbose(`Sequencer started`);
|
|
572
|
-
} else if (sequencer) {
|
|
573
|
-
log.warn(`Sequencer created but not started`);
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
// Create prover node subsystem if enabled
|
|
577
|
-
let proverNode: ProverNode | undefined;
|
|
578
|
-
if (config.enableProverNode) {
|
|
579
|
-
proverNode = await createProverNode(config, {
|
|
580
|
-
...deps.proverNodeDeps,
|
|
581
|
-
telemetry,
|
|
582
|
-
dateProvider,
|
|
583
|
-
archiver,
|
|
584
|
-
worldStateSynchronizer,
|
|
585
|
-
p2pClient,
|
|
927
|
+
feeProvider,
|
|
586
928
|
epochCache,
|
|
929
|
+
packageVersion,
|
|
930
|
+
peerProofVerifier,
|
|
931
|
+
rpcProofVerifier,
|
|
932
|
+
telemetry,
|
|
933
|
+
log,
|
|
587
934
|
blobClient,
|
|
935
|
+
validatorClient,
|
|
588
936
|
keyStoreManager,
|
|
589
|
-
|
|
937
|
+
debugLogStore,
|
|
938
|
+
);
|
|
590
939
|
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
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);
|
|
596
945
|
}
|
|
946
|
+
throw err;
|
|
597
947
|
}
|
|
598
|
-
|
|
599
|
-
const node = new AztecNodeService(
|
|
600
|
-
config,
|
|
601
|
-
p2pClient,
|
|
602
|
-
archiver,
|
|
603
|
-
archiver,
|
|
604
|
-
archiver,
|
|
605
|
-
archiver,
|
|
606
|
-
worldStateSynchronizer,
|
|
607
|
-
sequencer,
|
|
608
|
-
proverNode,
|
|
609
|
-
slasherClient,
|
|
610
|
-
validatorsSentinel,
|
|
611
|
-
epochPruneWatcher,
|
|
612
|
-
ethereumChain.chainInfo.id,
|
|
613
|
-
config.rollupVersion,
|
|
614
|
-
globalVariableBuilder,
|
|
615
|
-
epochCache,
|
|
616
|
-
packageVersion,
|
|
617
|
-
peerProofVerifier,
|
|
618
|
-
rpcProofVerifier,
|
|
619
|
-
telemetry,
|
|
620
|
-
log,
|
|
621
|
-
blobClient,
|
|
622
|
-
validatorClient,
|
|
623
|
-
keyStoreManager,
|
|
624
|
-
debugLogStore,
|
|
625
|
-
);
|
|
626
|
-
|
|
627
|
-
return node;
|
|
628
948
|
}
|
|
629
949
|
|
|
630
950
|
/**
|
|
@@ -699,77 +1019,13 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
699
1019
|
return nodeInfo;
|
|
700
1020
|
}
|
|
701
1021
|
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
* @param block - The block parameter (block number, block hash, or 'latest').
|
|
705
|
-
* @returns The requested block.
|
|
706
|
-
*/
|
|
707
|
-
public async getBlock(block: BlockParameter): Promise<L2Block | undefined> {
|
|
708
|
-
if (BlockHash.isBlockHash(block)) {
|
|
709
|
-
return this.getBlockByHash(block);
|
|
710
|
-
}
|
|
711
|
-
const blockNumber = block === 'latest' ? await this.getBlockNumber() : (block as BlockNumber);
|
|
712
|
-
if (blockNumber === BlockNumber.ZERO) {
|
|
713
|
-
return this.buildInitialBlock();
|
|
714
|
-
}
|
|
715
|
-
return await this.blockSource.getL2Block(blockNumber);
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
/**
|
|
719
|
-
* Get a block specified by its hash.
|
|
720
|
-
* @param blockHash - The block hash being requested.
|
|
721
|
-
* @returns The requested block.
|
|
722
|
-
*/
|
|
723
|
-
public async getBlockByHash(blockHash: BlockHash): Promise<L2Block | undefined> {
|
|
724
|
-
const initialBlockHash = await this.#getInitialHeaderHash();
|
|
725
|
-
if (blockHash.equals(initialBlockHash)) {
|
|
726
|
-
return this.buildInitialBlock();
|
|
727
|
-
}
|
|
728
|
-
return await this.blockSource.getL2BlockByHash(blockHash);
|
|
729
|
-
}
|
|
730
|
-
|
|
731
|
-
private buildInitialBlock(): L2Block {
|
|
732
|
-
const initialHeader = this.worldStateSynchronizer.getCommitted().getInitialHeader();
|
|
733
|
-
return L2Block.empty(initialHeader);
|
|
734
|
-
}
|
|
735
|
-
|
|
736
|
-
/**
|
|
737
|
-
* Get a block specified by its archive root.
|
|
738
|
-
* @param archive - The archive root being requested.
|
|
739
|
-
* @returns The requested block.
|
|
740
|
-
*/
|
|
741
|
-
public async getBlockByArchive(archive: Fr): Promise<L2Block | undefined> {
|
|
742
|
-
return await this.blockSource.getL2BlockByArchive(archive);
|
|
743
|
-
}
|
|
744
|
-
|
|
745
|
-
/**
|
|
746
|
-
* Method to request blocks. Will attempt to return all requested blocks but will return only those available.
|
|
747
|
-
* @param from - The start of the range of blocks to return.
|
|
748
|
-
* @param limit - The maximum number of blocks to obtain.
|
|
749
|
-
* @returns The blocks requested.
|
|
750
|
-
*/
|
|
751
|
-
public async getBlocks(from: BlockNumber, limit: number): Promise<L2Block[]> {
|
|
752
|
-
return (await this.blockSource.getBlocks(from, BlockNumber(limit))) ?? [];
|
|
753
|
-
}
|
|
754
|
-
|
|
755
|
-
public async getCheckpoints(from: CheckpointNumber, limit: number): Promise<PublishedCheckpoint[]> {
|
|
756
|
-
return (await this.blockSource.getCheckpoints(from, limit)) ?? [];
|
|
757
|
-
}
|
|
758
|
-
|
|
759
|
-
public async getCheckpointedBlocks(from: BlockNumber, limit: number) {
|
|
760
|
-
return (await this.blockSource.getCheckpointedBlocks(from, limit)) ?? [];
|
|
761
|
-
}
|
|
762
|
-
|
|
763
|
-
public getCheckpointsDataForEpoch(epochNumber: EpochNumber) {
|
|
764
|
-
return this.blockSource.getCheckpointsDataForEpoch(epochNumber);
|
|
1022
|
+
public async getCurrentMinFees(): Promise<GasFees> {
|
|
1023
|
+
return await this.feeProvider.getCurrentMinFees();
|
|
765
1024
|
}
|
|
766
1025
|
|
|
767
|
-
/**
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
*/
|
|
771
|
-
public async getCurrentMinFees(): Promise<GasFees> {
|
|
772
|
-
return await this.globalVariableBuilder.getCurrentMinFees();
|
|
1026
|
+
/** Returns predicted min fees for the current slot and next N slots. */
|
|
1027
|
+
public async getPredictedMinFees(manaUsage?: ManaUsageEstimate): Promise<GasFees[]> {
|
|
1028
|
+
return await this.feeProvider.getPredictedMinFees(manaUsage);
|
|
773
1029
|
}
|
|
774
1030
|
|
|
775
1031
|
public async getMaxPriorityFees(): Promise<GasFees> {
|
|
@@ -780,26 +1036,6 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
780
1036
|
return GasFees.from({ feePerDaGas: 0n, feePerL2Gas: 0n });
|
|
781
1037
|
}
|
|
782
1038
|
|
|
783
|
-
/**
|
|
784
|
-
* Method to fetch the latest block number synchronized by the node.
|
|
785
|
-
* @returns The block number.
|
|
786
|
-
*/
|
|
787
|
-
public async getBlockNumber(): Promise<BlockNumber> {
|
|
788
|
-
return await this.blockSource.getBlockNumber();
|
|
789
|
-
}
|
|
790
|
-
|
|
791
|
-
public async getProvenBlockNumber(): Promise<BlockNumber> {
|
|
792
|
-
return await this.blockSource.getProvenBlockNumber();
|
|
793
|
-
}
|
|
794
|
-
|
|
795
|
-
public async getCheckpointedBlockNumber(): Promise<BlockNumber> {
|
|
796
|
-
return await this.blockSource.getCheckpointedL2BlockNumber();
|
|
797
|
-
}
|
|
798
|
-
|
|
799
|
-
public getCheckpointNumber(): Promise<CheckpointNumber> {
|
|
800
|
-
return this.blockSource.getCheckpointNumber();
|
|
801
|
-
}
|
|
802
|
-
|
|
803
1039
|
/**
|
|
804
1040
|
* Method to fetch the version of the package.
|
|
805
1041
|
* @returns The node package version
|
|
@@ -917,7 +1153,13 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
917
1153
|
throw new Error(`Invalid tx: ${reason}`);
|
|
918
1154
|
}
|
|
919
1155
|
|
|
920
|
-
|
|
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
|
+
}
|
|
921
1163
|
const duration = timer.ms();
|
|
922
1164
|
this.metrics.receivedTx(duration, true);
|
|
923
1165
|
this.log.info(`Received tx ${txHash} in ${duration}ms`, { txHash });
|
|
@@ -939,10 +1181,15 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
939
1181
|
// If the tx is in the pool but not in the archiver, it's pending.
|
|
940
1182
|
// This handles race conditions between archiver and p2p, where the archiver
|
|
941
1183
|
// has pruned the block in which a tx was mined, but p2p has not caught up yet.
|
|
942
|
-
receipt = new TxReceipt(txHash, TxStatus.PENDING, undefined, undefined);
|
|
1184
|
+
receipt = new TxReceipt(txHash, TxStatus.PENDING, /*executionResult=*/ undefined, /*error=*/ undefined);
|
|
943
1185
|
} else {
|
|
944
1186
|
// Otherwise, if we don't know the tx, we consider it dropped.
|
|
945
|
-
receipt = new TxReceipt(
|
|
1187
|
+
receipt = new TxReceipt(
|
|
1188
|
+
txHash,
|
|
1189
|
+
TxStatus.DROPPED,
|
|
1190
|
+
/*executionResult=*/ undefined,
|
|
1191
|
+
/*error=*/ 'Tx dropped by P2P node',
|
|
1192
|
+
);
|
|
946
1193
|
}
|
|
947
1194
|
|
|
948
1195
|
this.debugLogStore.decorateReceiptWithLogs(txHash.toString(), receipt);
|
|
@@ -959,6 +1206,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
959
1206
|
*/
|
|
960
1207
|
public async stop() {
|
|
961
1208
|
this.log.info(`Stopping Aztec Node`);
|
|
1209
|
+
await tryStop(this.attestationsBlockWatcher);
|
|
962
1210
|
await tryStop(this.validatorsSentinel);
|
|
963
1211
|
await tryStop(this.epochPruneWatcher);
|
|
964
1212
|
await tryStop(this.slasherClient);
|
|
@@ -1052,7 +1300,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1052
1300
|
);
|
|
1053
1301
|
|
|
1054
1302
|
// Build a map from block number to block hash
|
|
1055
|
-
const blockNumberToHash = new Map<BlockNumber,
|
|
1303
|
+
const blockNumberToHash = new Map<BlockNumber, BlockHash>();
|
|
1056
1304
|
for (let i = 0; i < uniqueBlockNumbers.length; i++) {
|
|
1057
1305
|
const blockHash = blockHashes[i];
|
|
1058
1306
|
if (blockHash === undefined) {
|
|
@@ -1070,13 +1318,13 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1070
1318
|
if (blockNumber === undefined) {
|
|
1071
1319
|
throw new Error(`Block number not found for leaf index ${index} in tree ${MerkleTreeId[treeId]}`);
|
|
1072
1320
|
}
|
|
1073
|
-
const
|
|
1074
|
-
if (
|
|
1321
|
+
const l2BlockHash = blockNumberToHash.get(blockNumber);
|
|
1322
|
+
if (l2BlockHash === undefined) {
|
|
1075
1323
|
throw new Error(`Block hash not found for block number ${blockNumber}`);
|
|
1076
1324
|
}
|
|
1077
1325
|
return {
|
|
1078
1326
|
l2BlockNumber: blockNumber,
|
|
1079
|
-
l2BlockHash
|
|
1327
|
+
l2BlockHash,
|
|
1080
1328
|
data: index,
|
|
1081
1329
|
};
|
|
1082
1330
|
});
|
|
@@ -1230,41 +1478,6 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1230
1478
|
return preimage.leaf.value;
|
|
1231
1479
|
}
|
|
1232
1480
|
|
|
1233
|
-
public async getBlockHeader(block: BlockParameter = 'latest'): Promise<BlockHeader | undefined> {
|
|
1234
|
-
if (BlockHash.isBlockHash(block)) {
|
|
1235
|
-
const initialBlockHash = await this.#getInitialHeaderHash();
|
|
1236
|
-
if (block.equals(initialBlockHash)) {
|
|
1237
|
-
// Block source doesn't handle initial header so we need to handle the case separately.
|
|
1238
|
-
return this.worldStateSynchronizer.getCommitted().getInitialHeader();
|
|
1239
|
-
}
|
|
1240
|
-
return this.blockSource.getBlockHeaderByHash(block);
|
|
1241
|
-
} else {
|
|
1242
|
-
// Block source doesn't handle initial header so we need to handle the case separately.
|
|
1243
|
-
const blockNumber = block === 'latest' ? await this.getBlockNumber() : (block as BlockNumber);
|
|
1244
|
-
if (blockNumber === BlockNumber.ZERO) {
|
|
1245
|
-
return this.worldStateSynchronizer.getCommitted().getInitialHeader();
|
|
1246
|
-
}
|
|
1247
|
-
return this.blockSource.getBlockHeader(block);
|
|
1248
|
-
}
|
|
1249
|
-
}
|
|
1250
|
-
|
|
1251
|
-
/**
|
|
1252
|
-
* Get a block header specified by its archive root.
|
|
1253
|
-
* @param archive - The archive root being requested.
|
|
1254
|
-
* @returns The requested block header.
|
|
1255
|
-
*/
|
|
1256
|
-
public async getBlockHeaderByArchive(archive: Fr): Promise<BlockHeader | undefined> {
|
|
1257
|
-
return await this.blockSource.getBlockHeaderByArchive(archive);
|
|
1258
|
-
}
|
|
1259
|
-
|
|
1260
|
-
public getBlockData(number: BlockNumber): Promise<BlockData | undefined> {
|
|
1261
|
-
return this.blockSource.getBlockData(number);
|
|
1262
|
-
}
|
|
1263
|
-
|
|
1264
|
-
public getBlockDataByArchive(archive: Fr): Promise<BlockData | undefined> {
|
|
1265
|
-
return this.blockSource.getBlockDataByArchive(archive);
|
|
1266
|
-
}
|
|
1267
|
-
|
|
1268
1481
|
/**
|
|
1269
1482
|
* Simulates the public part of a transaction with the current state.
|
|
1270
1483
|
* @param tx - The transaction to simulate.
|
|
@@ -1488,7 +1701,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1488
1701
|
return Promise.resolve();
|
|
1489
1702
|
}
|
|
1490
1703
|
|
|
1491
|
-
public async rollbackTo(targetBlock: BlockNumber, force?: boolean): Promise<void> {
|
|
1704
|
+
public async rollbackTo(targetBlock: BlockNumber, force?: boolean, resumeSync = true): Promise<void> {
|
|
1492
1705
|
const archiver = this.blockSource as Archiver;
|
|
1493
1706
|
if (!('rollbackTo' in archiver)) {
|
|
1494
1707
|
throw new Error('Archiver implementation does not support rollbacks.');
|
|
@@ -1518,9 +1731,13 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1518
1731
|
this.log.error(`Error during rollback`, err);
|
|
1519
1732
|
throw err;
|
|
1520
1733
|
} finally {
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1734
|
+
if (resumeSync) {
|
|
1735
|
+
this.log.info(`Resuming world state and archiver sync.`);
|
|
1736
|
+
this.worldStateSynchronizer.resumeSync();
|
|
1737
|
+
archiver.resume();
|
|
1738
|
+
} else {
|
|
1739
|
+
this.log.info(`Sync left paused after rollback (resumeSync=false).`);
|
|
1740
|
+
}
|
|
1524
1741
|
}
|
|
1525
1742
|
}
|
|
1526
1743
|
|
|
@@ -1537,19 +1754,12 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1537
1754
|
return Promise.resolve();
|
|
1538
1755
|
}
|
|
1539
1756
|
|
|
1540
|
-
public getSlashPayloads(): Promise<SlashPayloadRound[]> {
|
|
1541
|
-
if (!this.slasherClient) {
|
|
1542
|
-
throw new Error(`Slasher client not enabled`);
|
|
1543
|
-
}
|
|
1544
|
-
return this.slasherClient.getSlashPayloads();
|
|
1545
|
-
}
|
|
1546
|
-
|
|
1547
1757
|
public getSlashOffenses(round: bigint | 'all' | 'current'): Promise<Offense[]> {
|
|
1548
1758
|
if (!this.slasherClient) {
|
|
1549
1759
|
throw new Error(`Slasher client not enabled`);
|
|
1550
1760
|
}
|
|
1551
1761
|
if (round === 'all') {
|
|
1552
|
-
return this.slasherClient.
|
|
1762
|
+
return this.slasherClient.getOffenses();
|
|
1553
1763
|
} else {
|
|
1554
1764
|
return this.slasherClient.gatherOffensesForRound(round === 'current' ? undefined : BigInt(round));
|
|
1555
1765
|
}
|
|
@@ -1643,6 +1853,40 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1643
1853
|
this.log.info('Keystore reloaded: coinbase, feeRecipient, and attester keys updated');
|
|
1644
1854
|
}
|
|
1645
1855
|
|
|
1856
|
+
public async mineBlock(): Promise<void> {
|
|
1857
|
+
if (!this.sequencer) {
|
|
1858
|
+
throw new BadRequestError('Cannot mine block: no sequencer is running');
|
|
1859
|
+
}
|
|
1860
|
+
|
|
1861
|
+
const currentBlockNumber = await this.getBlockNumber();
|
|
1862
|
+
|
|
1863
|
+
// Use slot duration + 50% buffer as the timeout so this works on running networks too
|
|
1864
|
+
const { slotDuration } = await this.blockSource.getL1Constants();
|
|
1865
|
+
const timeoutSeconds = Math.ceil(slotDuration * 1.5);
|
|
1866
|
+
|
|
1867
|
+
// Temporarily set minTxsPerBlock to 0 so the sequencer produces a block even with no txs
|
|
1868
|
+
const originalMinTxsPerBlock = this.sequencer.getSequencer().getConfig().minTxsPerBlock;
|
|
1869
|
+
this.sequencer.updateConfig({ minTxsPerBlock: 0 });
|
|
1870
|
+
|
|
1871
|
+
try {
|
|
1872
|
+
// Trigger the sequencer to produce a block immediately
|
|
1873
|
+
void this.sequencer.trigger();
|
|
1874
|
+
|
|
1875
|
+
// Wait for the new L2 block to appear
|
|
1876
|
+
await retryUntil(
|
|
1877
|
+
async () => {
|
|
1878
|
+
const newBlockNumber = await this.getBlockNumber();
|
|
1879
|
+
return newBlockNumber > currentBlockNumber ? true : undefined;
|
|
1880
|
+
},
|
|
1881
|
+
'mineBlock',
|
|
1882
|
+
timeoutSeconds,
|
|
1883
|
+
0.1,
|
|
1884
|
+
);
|
|
1885
|
+
} finally {
|
|
1886
|
+
this.sequencer.updateConfig({ minTxsPerBlock: originalMinTxsPerBlock });
|
|
1887
|
+
}
|
|
1888
|
+
}
|
|
1889
|
+
|
|
1646
1890
|
#getInitialHeaderHash(): Promise<BlockHash> {
|
|
1647
1891
|
if (!this.initialHeaderHashPromise) {
|
|
1648
1892
|
this.initialHeaderHashPromise = this.worldStateSynchronizer.getCommitted().getInitialHeader().hash();
|
|
@@ -1674,25 +1918,28 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1674
1918
|
if (BlockHash.isBlockHash(block)) {
|
|
1675
1919
|
const initialBlockHash = await this.#getInitialHeaderHash();
|
|
1676
1920
|
if (block.equals(initialBlockHash)) {
|
|
1677
|
-
// Block
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
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();
|
|
1686
1933
|
}
|
|
1687
|
-
|
|
1688
|
-
blockNumber = header.getBlockNumber();
|
|
1689
1934
|
} else {
|
|
1690
1935
|
blockNumber = block as BlockNumber;
|
|
1691
1936
|
}
|
|
1692
1937
|
|
|
1693
1938
|
// Check it's within world state sync range
|
|
1694
1939
|
if (blockNumber > blockSyncedTo) {
|
|
1695
|
-
throw new Error(
|
|
1940
|
+
throw new Error(
|
|
1941
|
+
`Queried block ${inspectBlockParameter(block)} not yet synced by the node (node is synced upto ${blockSyncedTo}).`,
|
|
1942
|
+
);
|
|
1696
1943
|
}
|
|
1697
1944
|
this.log.debug(`Using snapshot for block ${blockNumber}, world state synced upto ${blockSyncedTo}`);
|
|
1698
1945
|
|
|
@@ -1701,9 +1948,10 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1701
1948
|
// Double-check world-state synced to the same block hash as was requested
|
|
1702
1949
|
if (BlockHash.isBlockHash(block)) {
|
|
1703
1950
|
const blockHash = await snapshot.getLeafValue(MerkleTreeId.ARCHIVE, BigInt(blockNumber));
|
|
1704
|
-
if (!blockHash || !
|
|
1951
|
+
if (!blockHash || !block.equals(blockHash)) {
|
|
1952
|
+
const initialBlockHash = await this.#getInitialHeaderHash();
|
|
1705
1953
|
throw new Error(
|
|
1706
|
-
`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.`,
|
|
1707
1955
|
);
|
|
1708
1956
|
}
|
|
1709
1957
|
}
|
|
@@ -1711,23 +1959,31 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1711
1959
|
return snapshot;
|
|
1712
1960
|
}
|
|
1713
1961
|
|
|
1714
|
-
/** Resolves
|
|
1962
|
+
/** Resolves any {@link BlockParameter} variant to a concrete block number. */
|
|
1715
1963
|
protected async resolveBlockNumber(block: BlockParameter): Promise<BlockNumber> {
|
|
1716
|
-
|
|
1717
|
-
|
|
1964
|
+
const resolved = await this.resolveBlockParameter(block);
|
|
1965
|
+
if (resolved.number !== undefined) {
|
|
1966
|
+
return resolved.number;
|
|
1718
1967
|
}
|
|
1719
|
-
if (
|
|
1968
|
+
if (resolved.hash !== undefined) {
|
|
1720
1969
|
const initialBlockHash = await this.#getInitialHeaderHash();
|
|
1721
|
-
if (
|
|
1970
|
+
if (resolved.hash.equals(initialBlockHash)) {
|
|
1722
1971
|
return BlockNumber.ZERO;
|
|
1723
1972
|
}
|
|
1724
|
-
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);
|
|
1725
1981
|
if (!header) {
|
|
1726
|
-
throw new Error(`Block
|
|
1982
|
+
throw new Error(`Block with archive ${resolved.archive.toString()} not found.`);
|
|
1727
1983
|
}
|
|
1728
1984
|
return header.getBlockNumber();
|
|
1729
1985
|
}
|
|
1730
|
-
|
|
1986
|
+
throw new BadRequestError(`Invalid BlockParameter: ${JSON.stringify(block)}`);
|
|
1731
1987
|
}
|
|
1732
1988
|
|
|
1733
1989
|
/**
|