@aztec/aztec-node 0.0.1-commit.f5d02921e → 0.0.1-commit.f7ea82942
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/server.d.ts +14 -15
- package/dest/aztec-node/server.d.ts.map +1 -1
- package/dest/aztec-node/server.js +293 -243
- package/dest/sentinel/sentinel.js +1 -1
- package/package.json +26 -26
- package/src/aztec-node/server.ts +381 -321
- package/src/sentinel/sentinel.ts +1 -1
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,
|
|
@@ -59,13 +65,14 @@ import type {
|
|
|
59
65
|
NodeInfo,
|
|
60
66
|
ProtocolContractAddresses,
|
|
61
67
|
} from '@aztec/stdlib/contract';
|
|
62
|
-
import { GasFees } from '@aztec/stdlib/gas';
|
|
68
|
+
import { GasFees, type ManaUsageEstimate } from '@aztec/stdlib/gas';
|
|
63
69
|
import { computePublicDataTreeLeafSlot } from '@aztec/stdlib/hash';
|
|
64
70
|
import {
|
|
65
71
|
type AztecNode,
|
|
66
72
|
type AztecNodeAdmin,
|
|
67
73
|
type AztecNodeAdminConfig,
|
|
68
74
|
AztecNodeAdminConfigSchema,
|
|
75
|
+
type AztecNodeDebug,
|
|
69
76
|
type GetContractClassLogsResponse,
|
|
70
77
|
type GetPublicLogsResponse,
|
|
71
78
|
} from '@aztec/stdlib/interfaces/client';
|
|
@@ -81,11 +88,12 @@ import {
|
|
|
81
88
|
import type { DebugLogStore, LogFilter, SiloedTag, Tag, TxScopedL2Log } from '@aztec/stdlib/logs';
|
|
82
89
|
import { InMemoryDebugLogStore, NullDebugLogStore } from '@aztec/stdlib/logs';
|
|
83
90
|
import { InboxLeaf, type L1ToL2MessageSource } from '@aztec/stdlib/messaging';
|
|
84
|
-
import type { Offense
|
|
85
|
-
import type { NullifierLeafPreimage,
|
|
91
|
+
import type { Offense } from '@aztec/stdlib/slashing';
|
|
92
|
+
import type { NullifierLeafPreimage, PublicDataTreeLeafPreimage } from '@aztec/stdlib/trees';
|
|
86
93
|
import { MerkleTreeId, NullifierMembershipWitness, PublicDataWitness } from '@aztec/stdlib/trees';
|
|
87
94
|
import {
|
|
88
95
|
type BlockHeader,
|
|
96
|
+
type FeeProvider,
|
|
89
97
|
type GlobalVariableBuilder as GlobalVariableBuilderInterface,
|
|
90
98
|
type IndexedTxEffect,
|
|
91
99
|
PublicSimulationOutput,
|
|
@@ -97,6 +105,7 @@ import {
|
|
|
97
105
|
} from '@aztec/stdlib/tx';
|
|
98
106
|
import { getPackageVersion } from '@aztec/stdlib/update-checker';
|
|
99
107
|
import type { SingleValidatorStats, ValidatorsStats } from '@aztec/stdlib/validators';
|
|
108
|
+
import type { GenesisData } from '@aztec/stdlib/world-state';
|
|
100
109
|
import {
|
|
101
110
|
Attributes,
|
|
102
111
|
type TelemetryClient,
|
|
@@ -126,7 +135,7 @@ import { NodeMetrics } from './node_metrics.js';
|
|
|
126
135
|
/**
|
|
127
136
|
* The aztec node.
|
|
128
137
|
*/
|
|
129
|
-
export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
138
|
+
export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDebug, Traceable {
|
|
130
139
|
private metrics: NodeMetrics;
|
|
131
140
|
private initialHeaderHashPromise: Promise<BlockHash> | undefined = undefined;
|
|
132
141
|
|
|
@@ -151,6 +160,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
151
160
|
protected readonly l1ChainId: number,
|
|
152
161
|
protected readonly version: number,
|
|
153
162
|
protected readonly globalVariableBuilder: GlobalVariableBuilderInterface,
|
|
163
|
+
protected readonly feeProvider: FeeProvider,
|
|
154
164
|
protected readonly epochCache: EpochCacheInterface,
|
|
155
165
|
protected readonly packageVersion: string,
|
|
156
166
|
private peerProofVerifier: ClientProtocolCircuitVerifier,
|
|
@@ -207,7 +217,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
207
217
|
slashingProtectionDb?: SlashingProtectionDatabase;
|
|
208
218
|
} = {},
|
|
209
219
|
options: {
|
|
210
|
-
|
|
220
|
+
genesis?: GenesisData;
|
|
211
221
|
dontStartSequencer?: boolean;
|
|
212
222
|
dontStartProverNode?: boolean;
|
|
213
223
|
} = {},
|
|
@@ -303,328 +313,344 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
303
313
|
|
|
304
314
|
const epochCache = await EpochCache.create(config.l1Contracts.rollupAddress, config, { dateProvider });
|
|
305
315
|
|
|
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',
|
|
316
|
+
// Track started resources so we can clean up on partial failure during node creation.
|
|
317
|
+
const started: { stop?(): Promise<void> | void }[] = [];
|
|
318
|
+
try {
|
|
319
|
+
const archiver = await createArchiver(
|
|
320
|
+
config,
|
|
321
|
+
{ blobClient, epochCache, telemetry, dateProvider },
|
|
322
|
+
{ blockUntilSync: !config.skipArchiverInitialSync },
|
|
338
323
|
);
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
324
|
+
started.push(archiver);
|
|
325
|
+
|
|
326
|
+
// now create the merkle trees and the world state synchronizer
|
|
327
|
+
const worldStateSynchronizer = await createWorldStateSynchronizer(config, archiver, options.genesis, telemetry);
|
|
328
|
+
started.push(worldStateSynchronizer);
|
|
329
|
+
const useRealVerifiers = config.realProofs || config.debugForceTxProofVerification;
|
|
330
|
+
let peerProofVerifier: ClientProtocolCircuitVerifier;
|
|
331
|
+
let rpcProofVerifier: ClientProtocolCircuitVerifier;
|
|
332
|
+
if (useRealVerifiers) {
|
|
333
|
+
peerProofVerifier = await BatchChonkVerifier.new(config, config.bbChonkVerifyMaxBatch, 'peer');
|
|
334
|
+
const rpcVerifier = await BBCircuitVerifier.new(config);
|
|
335
|
+
rpcProofVerifier = new QueuedIVCVerifier(rpcVerifier, config.numConcurrentIVCVerifiers);
|
|
336
|
+
} else {
|
|
337
|
+
peerProofVerifier = new TestCircuitVerifier(config.proverTestVerificationDelayMs);
|
|
338
|
+
rpcProofVerifier = new TestCircuitVerifier(config.proverTestVerificationDelayMs);
|
|
339
|
+
}
|
|
340
|
+
started.push(peerProofVerifier, rpcProofVerifier);
|
|
347
341
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
archiver,
|
|
352
|
-
peerProofVerifier,
|
|
353
|
-
worldStateSynchronizer,
|
|
354
|
-
epochCache,
|
|
355
|
-
packageVersion,
|
|
356
|
-
dateProvider,
|
|
357
|
-
telemetry,
|
|
358
|
-
deps.p2pClientDeps,
|
|
359
|
-
);
|
|
342
|
+
let debugLogStore: DebugLogStore;
|
|
343
|
+
if (!config.realProofs) {
|
|
344
|
+
log.warn(`Aztec node is accepting fake proofs`);
|
|
360
345
|
|
|
361
|
-
|
|
362
|
-
|
|
346
|
+
debugLogStore = new InMemoryDebugLogStore();
|
|
347
|
+
log.info(
|
|
348
|
+
'Aztec node started in test mode (realProofs set to false) hence debug logs from public functions will be collected and served',
|
|
349
|
+
);
|
|
350
|
+
} else {
|
|
351
|
+
debugLogStore = new NullDebugLogStore();
|
|
352
|
+
}
|
|
363
353
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
...config,
|
|
354
|
+
const globalVariableBuilderConfig = {
|
|
355
|
+
l1Contracts: config.l1Contracts,
|
|
356
|
+
ethereumSlotDuration: config.ethereumSlotDuration,
|
|
357
|
+
rollupVersion: BigInt(config.rollupVersion),
|
|
369
358
|
l1GenesisTime,
|
|
370
359
|
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);
|
|
360
|
+
};
|
|
403
361
|
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
validatorClient.getProposalHandler().register(p2pClient, true, archiver, getValidatorAddresses);
|
|
362
|
+
const globalVariableBuilder = new GlobalVariableBuilder(dateProvider, publicClient, globalVariableBuilderConfig);
|
|
363
|
+
const feeProvider = new FeeProviderImpl(dateProvider, publicClient, globalVariableBuilderConfig);
|
|
407
364
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
365
|
+
const proverOnly = config.enableProverNode && config.disableValidator;
|
|
366
|
+
if (proverOnly) {
|
|
367
|
+
log.info('Starting in prover-only mode: skipping validator, sequencer, sentinel, and slasher subsystems');
|
|
411
368
|
}
|
|
412
|
-
}
|
|
413
369
|
|
|
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,
|
|
370
|
+
// create the tx pool and the p2p client, which will need the l2 block source
|
|
371
|
+
const p2pClient = await createP2PClient(
|
|
372
|
+
config,
|
|
373
|
+
archiver,
|
|
374
|
+
peerProofVerifier,
|
|
375
|
+
worldStateSynchronizer,
|
|
424
376
|
epochCache,
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
p2pClient,
|
|
428
|
-
blobClient,
|
|
377
|
+
feeProvider,
|
|
378
|
+
packageVersion,
|
|
429
379
|
dateProvider,
|
|
430
380
|
telemetry,
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
381
|
+
deps.p2pClientDeps,
|
|
382
|
+
);
|
|
383
|
+
started.push(p2pClient);
|
|
384
|
+
|
|
385
|
+
// We'll accumulate sentinel watchers here
|
|
386
|
+
const watchers: Watcher[] = [];
|
|
387
|
+
|
|
388
|
+
// Create FullNodeCheckpointsBuilder for block proposal handling and tx validation.
|
|
389
|
+
// Override maxTxsPerCheckpoint with the validator-specific limit if set.
|
|
390
|
+
const validatorCheckpointsBuilder = new FullNodeCheckpointsBuilder(
|
|
391
|
+
{
|
|
392
|
+
...config,
|
|
393
|
+
l1GenesisTime,
|
|
394
|
+
slotDuration: Number(slotDuration),
|
|
395
|
+
rollupManaLimit,
|
|
396
|
+
maxTxsPerCheckpoint: config.validateMaxTxsPerCheckpoint,
|
|
397
|
+
},
|
|
398
|
+
worldStateSynchronizer,
|
|
399
|
+
archiver,
|
|
400
|
+
dateProvider,
|
|
401
|
+
telemetry,
|
|
402
|
+
);
|
|
439
403
|
|
|
440
|
-
|
|
441
|
-
let epochPruneWatcher: EpochPruneWatcher | undefined;
|
|
442
|
-
let attestationsBlockWatcher: AttestationsBlockWatcher | undefined;
|
|
404
|
+
let validatorClient: ValidatorClient | undefined;
|
|
443
405
|
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
406
|
+
if (!proverOnly) {
|
|
407
|
+
// Create validator client if required
|
|
408
|
+
validatorClient = await createValidatorClient(config, {
|
|
409
|
+
checkpointsBuilder: validatorCheckpointsBuilder,
|
|
410
|
+
worldState: worldStateSynchronizer,
|
|
411
|
+
p2pClient,
|
|
412
|
+
telemetry,
|
|
413
|
+
dateProvider,
|
|
414
|
+
epochCache,
|
|
415
|
+
blockSource: archiver,
|
|
416
|
+
l1ToL2MessageSource: archiver,
|
|
417
|
+
keyStoreManager,
|
|
418
|
+
blobClient,
|
|
419
|
+
slashingProtectionDb: deps.slashingProtectionDb,
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
// If we have a validator client, register it as a source of offenses for the slasher,
|
|
423
|
+
// and have it register callbacks on the p2p client *before* we start it, otherwise messages
|
|
424
|
+
// like attestations or auths will fail.
|
|
425
|
+
if (validatorClient) {
|
|
426
|
+
watchers.push(validatorClient);
|
|
427
|
+
|
|
428
|
+
const vc = validatorClient;
|
|
429
|
+
const getValidatorAddresses = () => vc.getValidatorAddresses().map(a => a.toString());
|
|
430
|
+
validatorClient.getProposalHandler().register(p2pClient, true, archiver, getValidatorAddresses);
|
|
431
|
+
|
|
432
|
+
if (!options.dontStartSequencer) {
|
|
433
|
+
await validatorClient.registerHandlers();
|
|
434
|
+
}
|
|
435
|
+
}
|
|
448
436
|
}
|
|
449
437
|
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
438
|
+
// If there's no validator client, create a ProposalHandler to handle block and checkpoint proposals
|
|
439
|
+
// for monitoring or reexecution. Reexecution (default) allows us to follow the pending chain,
|
|
440
|
+
// while non-reexecution is used for validating the proposals and collecting their txs.
|
|
441
|
+
// Checkpoint proposals rebuild blobs if the blob client can upload blobs.
|
|
442
|
+
if (!validatorClient) {
|
|
443
|
+
const reexecute = !!config.alwaysReexecuteBlockProposals;
|
|
444
|
+
log.info(`Setting up proposal handler` + (reexecute ? ' with reexecution of proposals' : ''));
|
|
445
|
+
createProposalHandler(config, {
|
|
446
|
+
checkpointsBuilder: validatorCheckpointsBuilder,
|
|
447
|
+
worldState: worldStateSynchronizer,
|
|
454
448
|
epochCache,
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
449
|
+
blockSource: archiver,
|
|
450
|
+
l1ToL2MessageSource: archiver,
|
|
451
|
+
p2pClient,
|
|
452
|
+
blobClient,
|
|
453
|
+
dateProvider,
|
|
454
|
+
telemetry,
|
|
455
|
+
}).register(p2pClient, reexecute, archiver);
|
|
460
456
|
}
|
|
461
457
|
|
|
462
|
-
//
|
|
463
|
-
|
|
464
|
-
attestationsBlockWatcher = new AttestationsBlockWatcher(archiver, epochCache, config);
|
|
465
|
-
watchers.push(attestationsBlockWatcher);
|
|
466
|
-
}
|
|
467
|
-
}
|
|
458
|
+
// Start world state and wait for it to sync to the archiver.
|
|
459
|
+
await worldStateSynchronizer.start();
|
|
468
460
|
|
|
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
|
-
});
|
|
461
|
+
// Start p2p. Note that it depends on world state to be running.
|
|
462
|
+
await p2pClient.start();
|
|
488
463
|
|
|
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();
|
|
464
|
+
let validatorsSentinel: Awaited<ReturnType<typeof createSentinel>> | undefined;
|
|
465
|
+
let epochPruneWatcher: EpochPruneWatcher | undefined;
|
|
466
|
+
let attestationsBlockWatcher: AttestationsBlockWatcher | undefined;
|
|
510
467
|
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
468
|
+
if (!proverOnly) {
|
|
469
|
+
validatorsSentinel = await createSentinel(epochCache, archiver, p2pClient, config);
|
|
470
|
+
if (validatorsSentinel && config.slashInactivityPenalty > 0n) {
|
|
471
|
+
watchers.push(validatorsSentinel);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
if (config.slashPrunePenalty > 0n || config.slashDataWithholdingPenalty > 0n) {
|
|
475
|
+
epochPruneWatcher = new EpochPruneWatcher(
|
|
476
|
+
archiver,
|
|
477
|
+
archiver,
|
|
478
|
+
epochCache,
|
|
479
|
+
p2pClient.getTxProvider(),
|
|
480
|
+
validatorCheckpointsBuilder,
|
|
481
|
+
config,
|
|
482
|
+
);
|
|
483
|
+
watchers.push(epochPruneWatcher);
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
// We assume we want to slash for invalid attestations unless all max penalties are set to 0
|
|
487
|
+
if (config.slashProposeInvalidAttestationsPenalty > 0n || config.slashAttestDescendantOfInvalidPenalty > 0n) {
|
|
488
|
+
attestationsBlockWatcher = new AttestationsBlockWatcher(archiver, epochCache, config);
|
|
489
|
+
watchers.push(attestationsBlockWatcher);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
// Start p2p-related services once the archiver has completed sync
|
|
494
|
+
void archiver
|
|
495
|
+
.waitForInitialSync()
|
|
496
|
+
.then(async () => {
|
|
497
|
+
await validatorsSentinel?.start();
|
|
498
|
+
await epochPruneWatcher?.start();
|
|
499
|
+
await attestationsBlockWatcher?.start();
|
|
500
|
+
log.info(`All p2p services started`);
|
|
501
|
+
})
|
|
502
|
+
.catch(err => log.error('Failed to start p2p services after archiver sync', err));
|
|
503
|
+
|
|
504
|
+
// Validator enabled, create/start relevant service
|
|
505
|
+
let sequencer: SequencerClient | undefined;
|
|
506
|
+
let slasherClient: SlasherClientInterface | undefined;
|
|
507
|
+
if (!config.disableValidator && validatorClient) {
|
|
508
|
+
// We create a slasher only if we have a sequencer, since all slashing actions go through the sequencer publisher
|
|
509
|
+
// as they are executed when the node is selected as proposer.
|
|
510
|
+
const validatorAddresses = keyStoreManager
|
|
511
|
+
? NodeKeystoreAdapter.fromKeyStoreManager(keyStoreManager).getAddresses()
|
|
512
|
+
: [];
|
|
513
|
+
|
|
514
|
+
slasherClient = await createSlasher(
|
|
515
|
+
config,
|
|
516
|
+
config.l1Contracts,
|
|
517
|
+
getPublicClient(config),
|
|
518
|
+
watchers,
|
|
519
|
+
dateProvider,
|
|
520
|
+
epochCache,
|
|
521
|
+
validatorAddresses,
|
|
522
|
+
undefined, // logger
|
|
523
|
+
);
|
|
524
|
+
await slasherClient.start();
|
|
525
|
+
started.push(slasherClient);
|
|
526
|
+
|
|
527
|
+
const l1TxUtils = config.sequencerPublisherForwarderAddress
|
|
528
|
+
? await createForwarderL1TxUtilsFromSigners(
|
|
529
|
+
publicClient,
|
|
530
|
+
keyStoreManager!.createAllValidatorPublisherSigners(),
|
|
531
|
+
config.sequencerPublisherForwarderAddress,
|
|
532
|
+
{ ...config, scope: 'sequencer' },
|
|
533
|
+
{ telemetry, logger: log.createChild('l1-tx-utils'), dateProvider, kzg: Blob.getViemKzgInstance() },
|
|
534
|
+
)
|
|
535
|
+
: await createL1TxUtilsFromSigners(
|
|
536
|
+
publicClient,
|
|
537
|
+
keyStoreManager!.createAllValidatorPublisherSigners(),
|
|
538
|
+
{ ...config, scope: 'sequencer' },
|
|
539
|
+
{ telemetry, logger: log.createChild('l1-tx-utils'), dateProvider, kzg: Blob.getViemKzgInstance() },
|
|
540
|
+
);
|
|
541
|
+
|
|
542
|
+
// Create a funder L1TxUtils from the keystore funding account (if configured)
|
|
543
|
+
const fundingSigner = keyStoreManager?.createFundingSigner();
|
|
544
|
+
let funderL1TxUtils: L1TxUtils | undefined;
|
|
545
|
+
if (fundingSigner) {
|
|
546
|
+
const [funder] = await createL1TxUtilsFromSigners(
|
|
520
547
|
publicClient,
|
|
521
|
-
|
|
548
|
+
[fundingSigner],
|
|
522
549
|
{ ...config, scope: 'sequencer' },
|
|
523
|
-
{ telemetry, logger: log.createChild('l1-tx-utils'), dateProvider
|
|
550
|
+
{ telemetry, logger: log.createChild('l1-tx-utils:funder'), dateProvider },
|
|
524
551
|
);
|
|
552
|
+
funderL1TxUtils = funder;
|
|
553
|
+
}
|
|
525
554
|
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
{ telemetry, logger: log.createChild('l1-tx-utils:funder'), dateProvider },
|
|
555
|
+
// Create and start the sequencer client
|
|
556
|
+
const checkpointsBuilder = new CheckpointsBuilder(
|
|
557
|
+
{ ...config, l1GenesisTime, slotDuration: Number(slotDuration), rollupManaLimit },
|
|
558
|
+
worldStateSynchronizer,
|
|
559
|
+
archiver,
|
|
560
|
+
dateProvider,
|
|
561
|
+
telemetry,
|
|
562
|
+
debugLogStore,
|
|
535
563
|
);
|
|
536
|
-
|
|
564
|
+
|
|
565
|
+
sequencer = await SequencerClient.new(config, {
|
|
566
|
+
...deps,
|
|
567
|
+
epochCache,
|
|
568
|
+
l1TxUtils,
|
|
569
|
+
funderL1TxUtils,
|
|
570
|
+
validatorClient,
|
|
571
|
+
p2pClient,
|
|
572
|
+
worldStateSynchronizer,
|
|
573
|
+
slasherClient,
|
|
574
|
+
checkpointsBuilder,
|
|
575
|
+
l2BlockSource: archiver,
|
|
576
|
+
l1ToL2MessageSource: archiver,
|
|
577
|
+
telemetry,
|
|
578
|
+
dateProvider,
|
|
579
|
+
blobClient,
|
|
580
|
+
nodeKeyStore: keyStoreManager!,
|
|
581
|
+
globalVariableBuilder,
|
|
582
|
+
});
|
|
537
583
|
}
|
|
538
584
|
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
debugLogStore,
|
|
547
|
-
);
|
|
585
|
+
if (!options.dontStartSequencer && sequencer) {
|
|
586
|
+
await sequencer.start();
|
|
587
|
+
started.push(sequencer);
|
|
588
|
+
log.verbose(`Sequencer started`);
|
|
589
|
+
} else if (sequencer) {
|
|
590
|
+
log.warn(`Sequencer created but not started`);
|
|
591
|
+
}
|
|
548
592
|
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
593
|
+
// Create prover node subsystem if enabled
|
|
594
|
+
let proverNode: ProverNode | undefined;
|
|
595
|
+
if (config.enableProverNode) {
|
|
596
|
+
proverNode = await createProverNode(config, {
|
|
597
|
+
...deps.proverNodeDeps,
|
|
598
|
+
telemetry,
|
|
599
|
+
dateProvider,
|
|
600
|
+
archiver,
|
|
601
|
+
worldStateSynchronizer,
|
|
602
|
+
p2pClient,
|
|
603
|
+
epochCache,
|
|
604
|
+
blobClient,
|
|
605
|
+
keyStoreManager,
|
|
606
|
+
});
|
|
607
|
+
|
|
608
|
+
if (!options.dontStartProverNode) {
|
|
609
|
+
await proverNode.start();
|
|
610
|
+
started.push(proverNode);
|
|
611
|
+
log.info(`Prover node subsystem started`);
|
|
612
|
+
} else {
|
|
613
|
+
log.info(`Prover node subsystem created but not started`);
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
const node = new AztecNodeService(
|
|
618
|
+
config,
|
|
555
619
|
p2pClient,
|
|
620
|
+
archiver,
|
|
621
|
+
archiver,
|
|
622
|
+
archiver,
|
|
623
|
+
archiver,
|
|
556
624
|
worldStateSynchronizer,
|
|
625
|
+
sequencer,
|
|
626
|
+
proverNode,
|
|
557
627
|
slasherClient,
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
dateProvider,
|
|
563
|
-
blobClient,
|
|
564
|
-
nodeKeyStore: keyStoreManager!,
|
|
628
|
+
validatorsSentinel,
|
|
629
|
+
epochPruneWatcher,
|
|
630
|
+
ethereumChain.chainInfo.id,
|
|
631
|
+
config.rollupVersion,
|
|
565
632
|
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,
|
|
633
|
+
feeProvider,
|
|
586
634
|
epochCache,
|
|
635
|
+
packageVersion,
|
|
636
|
+
peerProofVerifier,
|
|
637
|
+
rpcProofVerifier,
|
|
638
|
+
telemetry,
|
|
639
|
+
log,
|
|
587
640
|
blobClient,
|
|
641
|
+
validatorClient,
|
|
588
642
|
keyStoreManager,
|
|
589
|
-
|
|
643
|
+
debugLogStore,
|
|
644
|
+
);
|
|
590
645
|
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
646
|
+
return node;
|
|
647
|
+
} catch (err) {
|
|
648
|
+
log.error('Failed during node creation, stopping started resources', err);
|
|
649
|
+
for (const resource of started.reverse()) {
|
|
650
|
+
await tryStop(resource);
|
|
596
651
|
}
|
|
652
|
+
throw err;
|
|
597
653
|
}
|
|
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
654
|
}
|
|
629
655
|
|
|
630
656
|
/**
|
|
@@ -764,12 +790,13 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
764
790
|
return this.blockSource.getCheckpointsDataForEpoch(epochNumber);
|
|
765
791
|
}
|
|
766
792
|
|
|
767
|
-
/**
|
|
768
|
-
* Method to fetch the current min L2 fees.
|
|
769
|
-
* @returns The current min L2 fees.
|
|
770
|
-
*/
|
|
771
793
|
public async getCurrentMinFees(): Promise<GasFees> {
|
|
772
|
-
return await this.
|
|
794
|
+
return await this.feeProvider.getCurrentMinFees();
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
/** Returns predicted min fees for the current slot and next N slots. */
|
|
798
|
+
public async getPredictedMinFees(manaUsage?: ManaUsageEstimate): Promise<GasFees[]> {
|
|
799
|
+
return await this.feeProvider.getPredictedMinFees(manaUsage);
|
|
773
800
|
}
|
|
774
801
|
|
|
775
802
|
public async getMaxPriorityFees(): Promise<GasFees> {
|
|
@@ -1052,7 +1079,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1052
1079
|
);
|
|
1053
1080
|
|
|
1054
1081
|
// Build a map from block number to block hash
|
|
1055
|
-
const blockNumberToHash = new Map<BlockNumber,
|
|
1082
|
+
const blockNumberToHash = new Map<BlockNumber, BlockHash>();
|
|
1056
1083
|
for (let i = 0; i < uniqueBlockNumbers.length; i++) {
|
|
1057
1084
|
const blockHash = blockHashes[i];
|
|
1058
1085
|
if (blockHash === undefined) {
|
|
@@ -1070,13 +1097,13 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1070
1097
|
if (blockNumber === undefined) {
|
|
1071
1098
|
throw new Error(`Block number not found for leaf index ${index} in tree ${MerkleTreeId[treeId]}`);
|
|
1072
1099
|
}
|
|
1073
|
-
const
|
|
1074
|
-
if (
|
|
1100
|
+
const l2BlockHash = blockNumberToHash.get(blockNumber);
|
|
1101
|
+
if (l2BlockHash === undefined) {
|
|
1075
1102
|
throw new Error(`Block hash not found for block number ${blockNumber}`);
|
|
1076
1103
|
}
|
|
1077
1104
|
return {
|
|
1078
1105
|
l2BlockNumber: blockNumber,
|
|
1079
|
-
l2BlockHash
|
|
1106
|
+
l2BlockHash,
|
|
1080
1107
|
data: index,
|
|
1081
1108
|
};
|
|
1082
1109
|
});
|
|
@@ -1488,7 +1515,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1488
1515
|
return Promise.resolve();
|
|
1489
1516
|
}
|
|
1490
1517
|
|
|
1491
|
-
public async rollbackTo(targetBlock: BlockNumber, force?: boolean): Promise<void> {
|
|
1518
|
+
public async rollbackTo(targetBlock: BlockNumber, force?: boolean, resumeSync = true): Promise<void> {
|
|
1492
1519
|
const archiver = this.blockSource as Archiver;
|
|
1493
1520
|
if (!('rollbackTo' in archiver)) {
|
|
1494
1521
|
throw new Error('Archiver implementation does not support rollbacks.');
|
|
@@ -1518,9 +1545,13 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1518
1545
|
this.log.error(`Error during rollback`, err);
|
|
1519
1546
|
throw err;
|
|
1520
1547
|
} finally {
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1548
|
+
if (resumeSync) {
|
|
1549
|
+
this.log.info(`Resuming world state and archiver sync.`);
|
|
1550
|
+
this.worldStateSynchronizer.resumeSync();
|
|
1551
|
+
archiver.resume();
|
|
1552
|
+
} else {
|
|
1553
|
+
this.log.info(`Sync left paused after rollback (resumeSync=false).`);
|
|
1554
|
+
}
|
|
1524
1555
|
}
|
|
1525
1556
|
}
|
|
1526
1557
|
|
|
@@ -1537,19 +1568,12 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1537
1568
|
return Promise.resolve();
|
|
1538
1569
|
}
|
|
1539
1570
|
|
|
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
1571
|
public getSlashOffenses(round: bigint | 'all' | 'current'): Promise<Offense[]> {
|
|
1548
1572
|
if (!this.slasherClient) {
|
|
1549
1573
|
throw new Error(`Slasher client not enabled`);
|
|
1550
1574
|
}
|
|
1551
1575
|
if (round === 'all') {
|
|
1552
|
-
return this.slasherClient.
|
|
1576
|
+
return this.slasherClient.getOffenses();
|
|
1553
1577
|
} else {
|
|
1554
1578
|
return this.slasherClient.gatherOffensesForRound(round === 'current' ? undefined : BigInt(round));
|
|
1555
1579
|
}
|
|
@@ -1643,6 +1667,40 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1643
1667
|
this.log.info('Keystore reloaded: coinbase, feeRecipient, and attester keys updated');
|
|
1644
1668
|
}
|
|
1645
1669
|
|
|
1670
|
+
public async mineBlock(): Promise<void> {
|
|
1671
|
+
if (!this.sequencer) {
|
|
1672
|
+
throw new BadRequestError('Cannot mine block: no sequencer is running');
|
|
1673
|
+
}
|
|
1674
|
+
|
|
1675
|
+
const currentBlockNumber = await this.getBlockNumber();
|
|
1676
|
+
|
|
1677
|
+
// Use slot duration + 50% buffer as the timeout so this works on running networks too
|
|
1678
|
+
const { slotDuration } = await this.blockSource.getL1Constants();
|
|
1679
|
+
const timeoutSeconds = Math.ceil(slotDuration * 1.5);
|
|
1680
|
+
|
|
1681
|
+
// Temporarily set minTxsPerBlock to 0 so the sequencer produces a block even with no txs
|
|
1682
|
+
const originalMinTxsPerBlock = this.sequencer.getSequencer().getConfig().minTxsPerBlock;
|
|
1683
|
+
this.sequencer.updateConfig({ minTxsPerBlock: 0 });
|
|
1684
|
+
|
|
1685
|
+
try {
|
|
1686
|
+
// Trigger the sequencer to produce a block immediately
|
|
1687
|
+
void this.sequencer.trigger();
|
|
1688
|
+
|
|
1689
|
+
// Wait for the new L2 block to appear
|
|
1690
|
+
await retryUntil(
|
|
1691
|
+
async () => {
|
|
1692
|
+
const newBlockNumber = await this.getBlockNumber();
|
|
1693
|
+
return newBlockNumber > currentBlockNumber ? true : undefined;
|
|
1694
|
+
},
|
|
1695
|
+
'mineBlock',
|
|
1696
|
+
timeoutSeconds,
|
|
1697
|
+
0.1,
|
|
1698
|
+
);
|
|
1699
|
+
} finally {
|
|
1700
|
+
this.sequencer.updateConfig({ minTxsPerBlock: originalMinTxsPerBlock });
|
|
1701
|
+
}
|
|
1702
|
+
}
|
|
1703
|
+
|
|
1646
1704
|
#getInitialHeaderHash(): Promise<BlockHash> {
|
|
1647
1705
|
if (!this.initialHeaderHashPromise) {
|
|
1648
1706
|
this.initialHeaderHashPromise = this.worldStateSynchronizer.getCommitted().getInitialHeader().hash();
|
|
@@ -1674,18 +1732,19 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1674
1732
|
if (BlockHash.isBlockHash(block)) {
|
|
1675
1733
|
const initialBlockHash = await this.#getInitialHeaderHash();
|
|
1676
1734
|
if (block.equals(initialBlockHash)) {
|
|
1677
|
-
// Block
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1735
|
+
// Block 0 is a first-class historical block: its state lives in the trees' persisted
|
|
1736
|
+
// block-0 payload. Resolving the genesis hash to block number 0 lets the snapshot path
|
|
1737
|
+
// pin reads to genesis state even after the node has advanced past it.
|
|
1738
|
+
blockNumber = BlockNumber.ZERO;
|
|
1739
|
+
} else {
|
|
1740
|
+
const header = await this.blockSource.getBlockHeaderByHash(block);
|
|
1741
|
+
if (!header) {
|
|
1742
|
+
throw new Error(
|
|
1743
|
+
`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.`,
|
|
1744
|
+
);
|
|
1745
|
+
}
|
|
1746
|
+
blockNumber = header.getBlockNumber();
|
|
1686
1747
|
}
|
|
1687
|
-
|
|
1688
|
-
blockNumber = header.getBlockNumber();
|
|
1689
1748
|
} else {
|
|
1690
1749
|
blockNumber = block as BlockNumber;
|
|
1691
1750
|
}
|
|
@@ -1701,9 +1760,10 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1701
1760
|
// Double-check world-state synced to the same block hash as was requested
|
|
1702
1761
|
if (BlockHash.isBlockHash(block)) {
|
|
1703
1762
|
const blockHash = await snapshot.getLeafValue(MerkleTreeId.ARCHIVE, BigInt(blockNumber));
|
|
1704
|
-
if (!blockHash || !
|
|
1763
|
+
if (!blockHash || !block.equals(blockHash)) {
|
|
1764
|
+
const initialBlockHash = await this.#getInitialHeaderHash();
|
|
1705
1765
|
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.`,
|
|
1766
|
+
`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
1767
|
);
|
|
1708
1768
|
}
|
|
1709
1769
|
}
|