@aztec/aztec-node 0.0.1-commit.b1c78909e → 0.0.1-commit.b2a5d0dd1
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 +29 -31
- package/dest/aztec-node/server.d.ts.map +1 -1
- package/dest/aztec-node/server.js +397 -276
- package/dest/sentinel/sentinel.js +6 -6
- package/package.json +26 -27
- package/src/aztec-node/server.ts +485 -350
- package/src/sentinel/sentinel.ts +6 -6
package/src/aztec-node/server.ts
CHANGED
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
import { Archiver, createArchiver } from '@aztec/archiver';
|
|
2
|
-
import { BBCircuitVerifier,
|
|
2
|
+
import { BBCircuitVerifier, BatchChonkVerifier, QueuedIVCVerifier } from '@aztec/bb-prover';
|
|
3
|
+
import { TestCircuitVerifier } from '@aztec/bb-prover/test';
|
|
3
4
|
import { type BlobClientInterface, createBlobClientWithFileStores } from '@aztec/blob-client/client';
|
|
4
5
|
import { Blob } from '@aztec/blob-lib';
|
|
5
6
|
import { ARCHIVE_HEIGHT, type L1_TO_L2_MSG_TREE_HEIGHT, type NOTE_HASH_TREE_HEIGHT } from '@aztec/constants';
|
|
6
7
|
import { EpochCache, type EpochCacheInterface } from '@aztec/epoch-cache';
|
|
7
8
|
import { createEthereumChain } from '@aztec/ethereum/chain';
|
|
8
|
-
import { getPublicClient } from '@aztec/ethereum/client';
|
|
9
|
+
import { getPublicClient, makeL1HttpTransport } from '@aztec/ethereum/client';
|
|
9
10
|
import { RegistryContract, RollupContract } from '@aztec/ethereum/contracts';
|
|
10
11
|
import type { L1ContractAddresses } from '@aztec/ethereum/l1-contract-addresses';
|
|
12
|
+
import type { L1TxUtils } from '@aztec/ethereum/l1-tx-utils';
|
|
11
13
|
import { BlockNumber, CheckpointNumber, EpochNumber, SlotNumber } from '@aztec/foundation/branded-types';
|
|
12
|
-
import { compactArray, pick, unique } from '@aztec/foundation/collection';
|
|
14
|
+
import { chunkBy, compactArray, pick, unique } from '@aztec/foundation/collection';
|
|
13
15
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
14
16
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
15
17
|
import { BadRequestError } from '@aztec/foundation/json-rpc';
|
|
16
18
|
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
19
|
+
import { retryUntil } from '@aztec/foundation/retry';
|
|
17
20
|
import { count } from '@aztec/foundation/string';
|
|
18
21
|
import { DateProvider, Timer } from '@aztec/foundation/timer';
|
|
19
22
|
import { MembershipWitness, SiblingPath } from '@aztec/foundation/trees';
|
|
@@ -30,7 +33,12 @@ import {
|
|
|
30
33
|
import { ProtocolContractAddress } from '@aztec/protocol-contracts';
|
|
31
34
|
import { type ProverNode, type ProverNodeDeps, createProverNode } from '@aztec/prover-node';
|
|
32
35
|
import { createKeyStoreForProver } from '@aztec/prover-node/config';
|
|
33
|
-
import {
|
|
36
|
+
import {
|
|
37
|
+
FeeProviderImpl,
|
|
38
|
+
GlobalVariableBuilder,
|
|
39
|
+
SequencerClient,
|
|
40
|
+
type SequencerPublisher,
|
|
41
|
+
} from '@aztec/sequencer-client';
|
|
34
42
|
import { PublicProcessorFactory } from '@aztec/simulator/server';
|
|
35
43
|
import {
|
|
36
44
|
AttestationsBlockWatcher,
|
|
@@ -57,13 +65,14 @@ import type {
|
|
|
57
65
|
NodeInfo,
|
|
58
66
|
ProtocolContractAddresses,
|
|
59
67
|
} from '@aztec/stdlib/contract';
|
|
60
|
-
import { GasFees } from '@aztec/stdlib/gas';
|
|
68
|
+
import { GasFees, type ManaUsageEstimate } from '@aztec/stdlib/gas';
|
|
61
69
|
import { computePublicDataTreeLeafSlot } from '@aztec/stdlib/hash';
|
|
62
70
|
import {
|
|
63
71
|
type AztecNode,
|
|
64
72
|
type AztecNodeAdmin,
|
|
65
73
|
type AztecNodeAdminConfig,
|
|
66
74
|
AztecNodeAdminConfigSchema,
|
|
75
|
+
type AztecNodeDebug,
|
|
67
76
|
type GetContractClassLogsResponse,
|
|
68
77
|
type GetPublicLogsResponse,
|
|
69
78
|
} from '@aztec/stdlib/interfaces/client';
|
|
@@ -79,11 +88,12 @@ import {
|
|
|
79
88
|
import type { DebugLogStore, LogFilter, SiloedTag, Tag, TxScopedL2Log } from '@aztec/stdlib/logs';
|
|
80
89
|
import { InMemoryDebugLogStore, NullDebugLogStore } from '@aztec/stdlib/logs';
|
|
81
90
|
import { InboxLeaf, type L1ToL2MessageSource } from '@aztec/stdlib/messaging';
|
|
82
|
-
import type { Offense
|
|
83
|
-
import type { NullifierLeafPreimage,
|
|
91
|
+
import type { Offense } from '@aztec/stdlib/slashing';
|
|
92
|
+
import type { NullifierLeafPreimage, PublicDataTreeLeafPreimage } from '@aztec/stdlib/trees';
|
|
84
93
|
import { MerkleTreeId, NullifierMembershipWitness, PublicDataWitness } from '@aztec/stdlib/trees';
|
|
85
94
|
import {
|
|
86
95
|
type BlockHeader,
|
|
96
|
+
type FeeProvider,
|
|
87
97
|
type GlobalVariableBuilder as GlobalVariableBuilderInterface,
|
|
88
98
|
type IndexedTxEffect,
|
|
89
99
|
PublicSimulationOutput,
|
|
@@ -95,6 +105,7 @@ import {
|
|
|
95
105
|
} from '@aztec/stdlib/tx';
|
|
96
106
|
import { getPackageVersion } from '@aztec/stdlib/update-checker';
|
|
97
107
|
import type { SingleValidatorStats, ValidatorsStats } from '@aztec/stdlib/validators';
|
|
108
|
+
import type { GenesisData } from '@aztec/stdlib/world-state';
|
|
98
109
|
import {
|
|
99
110
|
Attributes,
|
|
100
111
|
type TelemetryClient,
|
|
@@ -108,12 +119,13 @@ import {
|
|
|
108
119
|
FullNodeCheckpointsBuilder,
|
|
109
120
|
NodeKeystoreAdapter,
|
|
110
121
|
ValidatorClient,
|
|
111
|
-
|
|
122
|
+
createProposalHandler,
|
|
112
123
|
createValidatorClient,
|
|
113
124
|
} from '@aztec/validator-client';
|
|
125
|
+
import type { SlashingProtectionDatabase } from '@aztec/validator-ha-signer/types';
|
|
114
126
|
import { createWorldStateSynchronizer } from '@aztec/world-state';
|
|
115
127
|
|
|
116
|
-
import { createPublicClient
|
|
128
|
+
import { createPublicClient } from 'viem';
|
|
117
129
|
|
|
118
130
|
import { createSentinel } from '../sentinel/factory.js';
|
|
119
131
|
import { Sentinel } from '../sentinel/sentinel.js';
|
|
@@ -123,7 +135,7 @@ import { NodeMetrics } from './node_metrics.js';
|
|
|
123
135
|
/**
|
|
124
136
|
* The aztec node.
|
|
125
137
|
*/
|
|
126
|
-
export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
138
|
+
export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDebug, Traceable {
|
|
127
139
|
private metrics: NodeMetrics;
|
|
128
140
|
private initialHeaderHashPromise: Promise<BlockHash> | undefined = undefined;
|
|
129
141
|
|
|
@@ -148,9 +160,11 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
148
160
|
protected readonly l1ChainId: number,
|
|
149
161
|
protected readonly version: number,
|
|
150
162
|
protected readonly globalVariableBuilder: GlobalVariableBuilderInterface,
|
|
163
|
+
protected readonly feeProvider: FeeProvider,
|
|
151
164
|
protected readonly epochCache: EpochCacheInterface,
|
|
152
165
|
protected readonly packageVersion: string,
|
|
153
|
-
private
|
|
166
|
+
private peerProofVerifier: ClientProtocolCircuitVerifier,
|
|
167
|
+
private rpcProofVerifier: ClientProtocolCircuitVerifier,
|
|
154
168
|
private telemetry: TelemetryClient = getTelemetryClient(),
|
|
155
169
|
private log = createLogger('node'),
|
|
156
170
|
private blobClient?: BlobClientInterface,
|
|
@@ -172,6 +186,11 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
172
186
|
}
|
|
173
187
|
}
|
|
174
188
|
|
|
189
|
+
/** @internal Exposed for testing — returns the RPC proof verifier. */
|
|
190
|
+
public getProofVerifier(): ClientProtocolCircuitVerifier {
|
|
191
|
+
return this.rpcProofVerifier;
|
|
192
|
+
}
|
|
193
|
+
|
|
175
194
|
public async getWorldStateSyncStatus(): Promise<WorldStateSyncStatus> {
|
|
176
195
|
const status = await this.worldStateSynchronizer.status();
|
|
177
196
|
return status.syncSummary;
|
|
@@ -195,9 +214,10 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
195
214
|
dateProvider?: DateProvider;
|
|
196
215
|
p2pClientDeps?: P2PClientDeps;
|
|
197
216
|
proverNodeDeps?: Partial<ProverNodeDeps>;
|
|
217
|
+
slashingProtectionDb?: SlashingProtectionDatabase;
|
|
198
218
|
} = {},
|
|
199
219
|
options: {
|
|
200
|
-
|
|
220
|
+
genesis?: GenesisData;
|
|
201
221
|
dontStartSequencer?: boolean;
|
|
202
222
|
dontStartProverNode?: boolean;
|
|
203
223
|
} = {},
|
|
@@ -257,7 +277,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
257
277
|
|
|
258
278
|
const publicClient = createPublicClient({
|
|
259
279
|
chain: ethereumChain.chainInfo,
|
|
260
|
-
transport:
|
|
280
|
+
transport: makeL1HttpTransport(config.l1RpcUrls, { timeout: config.l1HttpTimeoutMS }),
|
|
261
281
|
pollingInterval: config.viemPollingIntervalMS,
|
|
262
282
|
});
|
|
263
283
|
|
|
@@ -293,298 +313,344 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
293
313
|
|
|
294
314
|
const epochCache = await EpochCache.create(config.l1Contracts.rollupAddress, config, { dateProvider });
|
|
295
315
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
const worldStateSynchronizer = await createWorldStateSynchronizer(
|
|
304
|
-
config,
|
|
305
|
-
archiver,
|
|
306
|
-
options.prefilledPublicData,
|
|
307
|
-
telemetry,
|
|
308
|
-
);
|
|
309
|
-
const circuitVerifier =
|
|
310
|
-
config.realProofs || config.debugForceTxProofVerification
|
|
311
|
-
? await BBCircuitVerifier.new(config)
|
|
312
|
-
: new TestCircuitVerifier(config.proverTestVerificationDelayMs);
|
|
313
|
-
|
|
314
|
-
let debugLogStore: DebugLogStore;
|
|
315
|
-
if (!config.realProofs) {
|
|
316
|
-
log.warn(`Aztec node is accepting fake proofs`);
|
|
317
|
-
|
|
318
|
-
debugLogStore = new InMemoryDebugLogStore();
|
|
319
|
-
log.info(
|
|
320
|
-
'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 },
|
|
321
323
|
);
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
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);
|
|
332
341
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
archiver,
|
|
337
|
-
proofVerifier,
|
|
338
|
-
worldStateSynchronizer,
|
|
339
|
-
epochCache,
|
|
340
|
-
packageVersion,
|
|
341
|
-
dateProvider,
|
|
342
|
-
telemetry,
|
|
343
|
-
deps.p2pClientDeps,
|
|
344
|
-
);
|
|
342
|
+
let debugLogStore: DebugLogStore;
|
|
343
|
+
if (!config.realProofs) {
|
|
344
|
+
log.warn(`Aztec node is accepting fake proofs`);
|
|
345
345
|
|
|
346
|
-
|
|
347
|
-
|
|
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
|
+
}
|
|
348
353
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
...config,
|
|
354
|
+
const globalVariableBuilderConfig = {
|
|
355
|
+
l1Contracts: config.l1Contracts,
|
|
356
|
+
ethereumSlotDuration: config.ethereumSlotDuration,
|
|
357
|
+
rollupVersion: BigInt(config.rollupVersion),
|
|
354
358
|
l1GenesisTime,
|
|
355
359
|
slotDuration: Number(slotDuration),
|
|
356
|
-
|
|
357
|
-
maxTxsPerCheckpoint: config.validateMaxTxsPerCheckpoint,
|
|
358
|
-
},
|
|
359
|
-
worldStateSynchronizer,
|
|
360
|
-
archiver,
|
|
361
|
-
dateProvider,
|
|
362
|
-
telemetry,
|
|
363
|
-
);
|
|
360
|
+
};
|
|
364
361
|
|
|
365
|
-
|
|
362
|
+
const globalVariableBuilder = new GlobalVariableBuilder(dateProvider, publicClient, globalVariableBuilderConfig);
|
|
363
|
+
const feeProvider = new FeeProviderImpl(dateProvider, publicClient, globalVariableBuilderConfig);
|
|
366
364
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
checkpointsBuilder: validatorCheckpointsBuilder,
|
|
371
|
-
worldState: worldStateSynchronizer,
|
|
372
|
-
p2pClient,
|
|
373
|
-
telemetry,
|
|
374
|
-
dateProvider,
|
|
375
|
-
epochCache,
|
|
376
|
-
blockSource: archiver,
|
|
377
|
-
l1ToL2MessageSource: archiver,
|
|
378
|
-
keyStoreManager,
|
|
379
|
-
blobClient,
|
|
380
|
-
});
|
|
381
|
-
|
|
382
|
-
// If we have a validator client, register it as a source of offenses for the slasher,
|
|
383
|
-
// and have it register callbacks on the p2p client *before* we start it, otherwise messages
|
|
384
|
-
// like attestations or auths will fail.
|
|
385
|
-
if (validatorClient) {
|
|
386
|
-
watchers.push(validatorClient);
|
|
387
|
-
if (!options.dontStartSequencer) {
|
|
388
|
-
await validatorClient.registerHandlers();
|
|
389
|
-
}
|
|
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');
|
|
390
368
|
}
|
|
391
|
-
}
|
|
392
369
|
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
createBlockProposalHandler(config, {
|
|
400
|
-
checkpointsBuilder: validatorCheckpointsBuilder,
|
|
401
|
-
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,
|
|
402
376
|
epochCache,
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
p2pClient,
|
|
377
|
+
feeProvider,
|
|
378
|
+
packageVersion,
|
|
406
379
|
dateProvider,
|
|
407
380
|
telemetry,
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
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
|
+
);
|
|
416
403
|
|
|
417
|
-
|
|
418
|
-
let epochPruneWatcher: EpochPruneWatcher | undefined;
|
|
419
|
-
let attestationsBlockWatcher: AttestationsBlockWatcher | undefined;
|
|
404
|
+
let validatorClient: ValidatorClient | undefined;
|
|
420
405
|
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
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
|
+
}
|
|
425
436
|
}
|
|
426
437
|
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
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,
|
|
431
448
|
epochCache,
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
449
|
+
blockSource: archiver,
|
|
450
|
+
l1ToL2MessageSource: archiver,
|
|
451
|
+
p2pClient,
|
|
452
|
+
blobClient,
|
|
453
|
+
dateProvider,
|
|
454
|
+
telemetry,
|
|
455
|
+
}).register(p2pClient, reexecute, archiver);
|
|
437
456
|
}
|
|
438
457
|
|
|
439
|
-
//
|
|
440
|
-
|
|
441
|
-
attestationsBlockWatcher = new AttestationsBlockWatcher(archiver, epochCache, config);
|
|
442
|
-
watchers.push(attestationsBlockWatcher);
|
|
443
|
-
}
|
|
444
|
-
}
|
|
458
|
+
// Start world state and wait for it to sync to the archiver.
|
|
459
|
+
await worldStateSynchronizer.start();
|
|
445
460
|
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
.waitForInitialSync()
|
|
449
|
-
.then(async () => {
|
|
450
|
-
await p2pClient.start();
|
|
451
|
-
await validatorsSentinel?.start();
|
|
452
|
-
await epochPruneWatcher?.start();
|
|
453
|
-
await attestationsBlockWatcher?.start();
|
|
454
|
-
log.info(`All p2p services started`);
|
|
455
|
-
})
|
|
456
|
-
.catch(err => log.error('Failed to start p2p services after archiver sync', err));
|
|
457
|
-
|
|
458
|
-
// Validator enabled, create/start relevant service
|
|
459
|
-
let sequencer: SequencerClient | undefined;
|
|
460
|
-
let slasherClient: SlasherClientInterface | undefined;
|
|
461
|
-
if (!config.disableValidator && validatorClient) {
|
|
462
|
-
// We create a slasher only if we have a sequencer, since all slashing actions go through the sequencer publisher
|
|
463
|
-
// as they are executed when the node is selected as proposer.
|
|
464
|
-
const validatorAddresses = keyStoreManager
|
|
465
|
-
? NodeKeystoreAdapter.fromKeyStoreManager(keyStoreManager).getAddresses()
|
|
466
|
-
: [];
|
|
467
|
-
|
|
468
|
-
slasherClient = await createSlasher(
|
|
469
|
-
config,
|
|
470
|
-
config.l1Contracts,
|
|
471
|
-
getPublicClient(config),
|
|
472
|
-
watchers,
|
|
473
|
-
dateProvider,
|
|
474
|
-
epochCache,
|
|
475
|
-
validatorAddresses,
|
|
476
|
-
undefined, // logger
|
|
477
|
-
);
|
|
478
|
-
await slasherClient.start();
|
|
461
|
+
// Start p2p. Note that it depends on world state to be running.
|
|
462
|
+
await p2pClient.start();
|
|
479
463
|
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
)
|
|
488
|
-
|
|
464
|
+
let validatorsSentinel: Awaited<ReturnType<typeof createSentinel>> | undefined;
|
|
465
|
+
let epochPruneWatcher: EpochPruneWatcher | undefined;
|
|
466
|
+
let attestationsBlockWatcher: AttestationsBlockWatcher | undefined;
|
|
467
|
+
|
|
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(
|
|
489
547
|
publicClient,
|
|
490
|
-
|
|
548
|
+
[fundingSigner],
|
|
491
549
|
{ ...config, scope: 'sequencer' },
|
|
492
|
-
{ telemetry, logger: log.createChild('l1-tx-utils'), dateProvider
|
|
550
|
+
{ telemetry, logger: log.createChild('l1-tx-utils:funder'), dateProvider },
|
|
493
551
|
);
|
|
552
|
+
funderL1TxUtils = funder;
|
|
553
|
+
}
|
|
494
554
|
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
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,
|
|
563
|
+
);
|
|
504
564
|
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
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
|
+
});
|
|
583
|
+
}
|
|
522
584
|
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
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
|
+
}
|
|
529
592
|
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
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,
|
|
619
|
+
p2pClient,
|
|
620
|
+
archiver,
|
|
621
|
+
archiver,
|
|
622
|
+
archiver,
|
|
537
623
|
archiver,
|
|
538
624
|
worldStateSynchronizer,
|
|
539
|
-
|
|
625
|
+
sequencer,
|
|
626
|
+
proverNode,
|
|
627
|
+
slasherClient,
|
|
628
|
+
validatorsSentinel,
|
|
629
|
+
epochPruneWatcher,
|
|
630
|
+
ethereumChain.chainInfo.id,
|
|
631
|
+
config.rollupVersion,
|
|
632
|
+
globalVariableBuilder,
|
|
633
|
+
feeProvider,
|
|
540
634
|
epochCache,
|
|
635
|
+
packageVersion,
|
|
636
|
+
peerProofVerifier,
|
|
637
|
+
rpcProofVerifier,
|
|
638
|
+
telemetry,
|
|
639
|
+
log,
|
|
541
640
|
blobClient,
|
|
641
|
+
validatorClient,
|
|
542
642
|
keyStoreManager,
|
|
543
|
-
|
|
643
|
+
debugLogStore,
|
|
644
|
+
);
|
|
544
645
|
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
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);
|
|
550
651
|
}
|
|
652
|
+
throw err;
|
|
551
653
|
}
|
|
552
|
-
|
|
553
|
-
const globalVariableBuilder = new GlobalVariableBuilder({
|
|
554
|
-
...config,
|
|
555
|
-
rollupVersion: BigInt(config.rollupVersion),
|
|
556
|
-
l1GenesisTime,
|
|
557
|
-
slotDuration: Number(slotDuration),
|
|
558
|
-
});
|
|
559
|
-
|
|
560
|
-
const node = new AztecNodeService(
|
|
561
|
-
config,
|
|
562
|
-
p2pClient,
|
|
563
|
-
archiver,
|
|
564
|
-
archiver,
|
|
565
|
-
archiver,
|
|
566
|
-
archiver,
|
|
567
|
-
worldStateSynchronizer,
|
|
568
|
-
sequencer,
|
|
569
|
-
proverNode,
|
|
570
|
-
slasherClient,
|
|
571
|
-
validatorsSentinel,
|
|
572
|
-
epochPruneWatcher,
|
|
573
|
-
ethereumChain.chainInfo.id,
|
|
574
|
-
config.rollupVersion,
|
|
575
|
-
globalVariableBuilder,
|
|
576
|
-
epochCache,
|
|
577
|
-
packageVersion,
|
|
578
|
-
proofVerifier,
|
|
579
|
-
telemetry,
|
|
580
|
-
log,
|
|
581
|
-
blobClient,
|
|
582
|
-
validatorClient,
|
|
583
|
-
keyStoreManager,
|
|
584
|
-
debugLogStore,
|
|
585
|
-
);
|
|
586
|
-
|
|
587
|
-
return node;
|
|
588
654
|
}
|
|
589
655
|
|
|
590
656
|
/**
|
|
@@ -720,12 +786,17 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
720
786
|
return (await this.blockSource.getCheckpointedBlocks(from, limit)) ?? [];
|
|
721
787
|
}
|
|
722
788
|
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
789
|
+
public getCheckpointsDataForEpoch(epochNumber: EpochNumber) {
|
|
790
|
+
return this.blockSource.getCheckpointsDataForEpoch(epochNumber);
|
|
791
|
+
}
|
|
792
|
+
|
|
727
793
|
public async getCurrentMinFees(): Promise<GasFees> {
|
|
728
|
-
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);
|
|
729
800
|
}
|
|
730
801
|
|
|
731
802
|
public async getMaxPriorityFees(): Promise<GasFees> {
|
|
@@ -793,18 +864,22 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
793
864
|
page?: number,
|
|
794
865
|
referenceBlock?: BlockHash,
|
|
795
866
|
): Promise<TxScopedL2Log[][]> {
|
|
867
|
+
let upToBlockNumber: BlockNumber | undefined;
|
|
796
868
|
if (referenceBlock) {
|
|
797
869
|
const initialBlockHash = await this.#getInitialHeaderHash();
|
|
798
|
-
if (
|
|
870
|
+
if (referenceBlock.equals(initialBlockHash)) {
|
|
871
|
+
upToBlockNumber = BlockNumber(0);
|
|
872
|
+
} else {
|
|
799
873
|
const header = await this.blockSource.getBlockHeaderByHash(referenceBlock);
|
|
800
874
|
if (!header) {
|
|
801
875
|
throw new Error(
|
|
802
876
|
`Block ${referenceBlock.toString()} not found in the node. This might indicate a reorg has occurred.`,
|
|
803
877
|
);
|
|
804
878
|
}
|
|
879
|
+
upToBlockNumber = header.globalVariables.blockNumber;
|
|
805
880
|
}
|
|
806
881
|
}
|
|
807
|
-
return this.logsSource.getPrivateLogsByTags(tags, page);
|
|
882
|
+
return this.logsSource.getPrivateLogsByTags(tags, page, upToBlockNumber);
|
|
808
883
|
}
|
|
809
884
|
|
|
810
885
|
public async getPublicLogsByTagsFromContract(
|
|
@@ -813,18 +888,22 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
813
888
|
page?: number,
|
|
814
889
|
referenceBlock?: BlockHash,
|
|
815
890
|
): Promise<TxScopedL2Log[][]> {
|
|
891
|
+
let upToBlockNumber: BlockNumber | undefined;
|
|
816
892
|
if (referenceBlock) {
|
|
817
893
|
const initialBlockHash = await this.#getInitialHeaderHash();
|
|
818
|
-
if (
|
|
894
|
+
if (referenceBlock.equals(initialBlockHash)) {
|
|
895
|
+
upToBlockNumber = BlockNumber(0);
|
|
896
|
+
} else {
|
|
819
897
|
const header = await this.blockSource.getBlockHeaderByHash(referenceBlock);
|
|
820
898
|
if (!header) {
|
|
821
899
|
throw new Error(
|
|
822
900
|
`Block ${referenceBlock.toString()} not found in the node. This might indicate a reorg has occurred.`,
|
|
823
901
|
);
|
|
824
902
|
}
|
|
903
|
+
upToBlockNumber = header.globalVariables.blockNumber;
|
|
825
904
|
}
|
|
826
905
|
}
|
|
827
|
-
return this.logsSource.getPublicLogsByTagsFromContract(contractAddress, tags, page);
|
|
906
|
+
return this.logsSource.getPublicLogsByTagsFromContract(contractAddress, tags, page, upToBlockNumber);
|
|
828
907
|
}
|
|
829
908
|
|
|
830
909
|
/**
|
|
@@ -910,7 +989,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
910
989
|
await tryStop(this.validatorsSentinel);
|
|
911
990
|
await tryStop(this.epochPruneWatcher);
|
|
912
991
|
await tryStop(this.slasherClient);
|
|
913
|
-
await tryStop(this.
|
|
992
|
+
await Promise.all([tryStop(this.peerProofVerifier), tryStop(this.rpcProofVerifier)]);
|
|
914
993
|
await tryStop(this.sequencer);
|
|
915
994
|
await tryStop(this.proverNode);
|
|
916
995
|
await tryStop(this.p2pClient);
|
|
@@ -966,7 +1045,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
966
1045
|
treeId: MerkleTreeId,
|
|
967
1046
|
leafValues: Fr[],
|
|
968
1047
|
): Promise<(DataInBlock<bigint> | undefined)[]> {
|
|
969
|
-
const committedDb = await this
|
|
1048
|
+
const committedDb = await this.getWorldState(referenceBlock);
|
|
970
1049
|
const maybeIndices = await committedDb.findLeafIndices(
|
|
971
1050
|
treeId,
|
|
972
1051
|
leafValues.map(x => x.toBuffer()),
|
|
@@ -1000,7 +1079,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1000
1079
|
);
|
|
1001
1080
|
|
|
1002
1081
|
// Build a map from block number to block hash
|
|
1003
|
-
const blockNumberToHash = new Map<BlockNumber,
|
|
1082
|
+
const blockNumberToHash = new Map<BlockNumber, BlockHash>();
|
|
1004
1083
|
for (let i = 0; i < uniqueBlockNumbers.length; i++) {
|
|
1005
1084
|
const blockHash = blockHashes[i];
|
|
1006
1085
|
if (blockHash === undefined) {
|
|
@@ -1018,13 +1097,13 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1018
1097
|
if (blockNumber === undefined) {
|
|
1019
1098
|
throw new Error(`Block number not found for leaf index ${index} in tree ${MerkleTreeId[treeId]}`);
|
|
1020
1099
|
}
|
|
1021
|
-
const
|
|
1022
|
-
if (
|
|
1100
|
+
const l2BlockHash = blockNumberToHash.get(blockNumber);
|
|
1101
|
+
if (l2BlockHash === undefined) {
|
|
1023
1102
|
throw new Error(`Block hash not found for block number ${blockNumber}`);
|
|
1024
1103
|
}
|
|
1025
1104
|
return {
|
|
1026
1105
|
l2BlockNumber: blockNumber,
|
|
1027
|
-
l2BlockHash
|
|
1106
|
+
l2BlockHash,
|
|
1028
1107
|
data: index,
|
|
1029
1108
|
};
|
|
1030
1109
|
});
|
|
@@ -1034,7 +1113,15 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1034
1113
|
referenceBlock: BlockParameter,
|
|
1035
1114
|
blockHash: BlockHash,
|
|
1036
1115
|
): Promise<MembershipWitness<typeof ARCHIVE_HEIGHT> | undefined> {
|
|
1037
|
-
|
|
1116
|
+
// The Noir circuit checks the archive membership proof against `anchor_block_header.last_archive.root`,
|
|
1117
|
+
// which is the archive tree root BEFORE the anchor block was added (i.e. the state after block N-1).
|
|
1118
|
+
// So we need the world state at block N-1, not block N, to produce a sibling path matching that root.
|
|
1119
|
+
const referenceBlockNumber = await this.resolveBlockNumber(referenceBlock);
|
|
1120
|
+
if (referenceBlockNumber === BlockNumber.ZERO) {
|
|
1121
|
+
// Block 0 (the initial block) has an empty archive, so no membership witness can exist.
|
|
1122
|
+
return undefined;
|
|
1123
|
+
}
|
|
1124
|
+
const committedDb = await this.getWorldState(BlockNumber(referenceBlockNumber - 1));
|
|
1038
1125
|
const [pathAndIndex] = await committedDb.findSiblingPaths<MerkleTreeId.ARCHIVE>(MerkleTreeId.ARCHIVE, [blockHash]);
|
|
1039
1126
|
return pathAndIndex === undefined
|
|
1040
1127
|
? undefined
|
|
@@ -1045,7 +1132,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1045
1132
|
referenceBlock: BlockParameter,
|
|
1046
1133
|
noteHash: Fr,
|
|
1047
1134
|
): Promise<MembershipWitness<typeof NOTE_HASH_TREE_HEIGHT> | undefined> {
|
|
1048
|
-
const committedDb = await this
|
|
1135
|
+
const committedDb = await this.getWorldState(referenceBlock);
|
|
1049
1136
|
const [pathAndIndex] = await committedDb.findSiblingPaths<MerkleTreeId.NOTE_HASH_TREE>(
|
|
1050
1137
|
MerkleTreeId.NOTE_HASH_TREE,
|
|
1051
1138
|
[noteHash],
|
|
@@ -1059,7 +1146,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1059
1146
|
referenceBlock: BlockParameter,
|
|
1060
1147
|
l1ToL2Message: Fr,
|
|
1061
1148
|
): Promise<[bigint, SiblingPath<typeof L1_TO_L2_MSG_TREE_HEIGHT>] | undefined> {
|
|
1062
|
-
const db = await this
|
|
1149
|
+
const db = await this.getWorldState(referenceBlock);
|
|
1063
1150
|
const [witness] = await db.findSiblingPaths(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, [l1ToL2Message]);
|
|
1064
1151
|
if (!witness) {
|
|
1065
1152
|
return undefined;
|
|
@@ -1092,19 +1179,9 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1092
1179
|
public async getL2ToL1Messages(epoch: EpochNumber): Promise<Fr[][][][]> {
|
|
1093
1180
|
// Assumes `getCheckpointedBlocksForEpoch` returns blocks in ascending order of block number.
|
|
1094
1181
|
const checkpointedBlocks = await this.blockSource.getCheckpointedBlocksForEpoch(epoch);
|
|
1095
|
-
const blocksInCheckpoints
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
for (const checkpointedBlock of checkpointedBlocks) {
|
|
1099
|
-
const block = checkpointedBlock.block;
|
|
1100
|
-
const slotNumber = block.header.globalVariables.slotNumber;
|
|
1101
|
-
if (slotNumber !== previousSlotNumber) {
|
|
1102
|
-
checkpointIndex++;
|
|
1103
|
-
blocksInCheckpoints.push([]);
|
|
1104
|
-
previousSlotNumber = slotNumber;
|
|
1105
|
-
}
|
|
1106
|
-
blocksInCheckpoints[checkpointIndex].push(block);
|
|
1107
|
-
}
|
|
1182
|
+
const blocksInCheckpoints = chunkBy(checkpointedBlocks, cb => cb.block.header.globalVariables.slotNumber).map(
|
|
1183
|
+
group => group.map(cb => cb.block),
|
|
1184
|
+
);
|
|
1108
1185
|
return blocksInCheckpoints.map(blocks =>
|
|
1109
1186
|
blocks.map(block => block.body.txEffects.map(txEffect => txEffect.l2ToL1Msgs)),
|
|
1110
1187
|
);
|
|
@@ -1114,7 +1191,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1114
1191
|
referenceBlock: BlockParameter,
|
|
1115
1192
|
nullifier: Fr,
|
|
1116
1193
|
): Promise<NullifierMembershipWitness | undefined> {
|
|
1117
|
-
const db = await this
|
|
1194
|
+
const db = await this.getWorldState(referenceBlock);
|
|
1118
1195
|
const [witness] = await db.findSiblingPaths(MerkleTreeId.NULLIFIER_TREE, [nullifier.toBuffer()]);
|
|
1119
1196
|
if (!witness) {
|
|
1120
1197
|
return undefined;
|
|
@@ -1129,33 +1206,20 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1129
1206
|
return new NullifierMembershipWitness(index, leafPreimage as NullifierLeafPreimage, path);
|
|
1130
1207
|
}
|
|
1131
1208
|
|
|
1132
|
-
/**
|
|
1133
|
-
* Returns a low nullifier membership witness for a given nullifier at a given block.
|
|
1134
|
-
* @param referenceBlock - The block parameter (block number, block hash, or 'latest') at which to get the data
|
|
1135
|
-
* (which contains the root of the nullifier tree in which we are searching for the nullifier).
|
|
1136
|
-
* @param nullifier - Nullifier we try to find the low nullifier witness for.
|
|
1137
|
-
* @returns The low nullifier membership witness (if found).
|
|
1138
|
-
* @remarks Low nullifier witness can be used to perform a nullifier non-inclusion proof by leveraging the "linked
|
|
1139
|
-
* list structure" of leaves and proving that a lower nullifier is pointing to a bigger next value than the nullifier
|
|
1140
|
-
* we are trying to prove non-inclusion for.
|
|
1141
|
-
*
|
|
1142
|
-
* Note: This function returns the membership witness of the nullifier itself and not the low nullifier when
|
|
1143
|
-
* the nullifier already exists in the tree. This is because the `getPreviousValueIndex` function returns the
|
|
1144
|
-
* index of the nullifier itself when it already exists in the tree.
|
|
1145
|
-
* TODO: This is a confusing behavior and we should eventually address that.
|
|
1146
|
-
*/
|
|
1147
1209
|
public async getLowNullifierMembershipWitness(
|
|
1148
1210
|
referenceBlock: BlockParameter,
|
|
1149
1211
|
nullifier: Fr,
|
|
1150
1212
|
): Promise<NullifierMembershipWitness | undefined> {
|
|
1151
|
-
const committedDb = await this
|
|
1213
|
+
const committedDb = await this.getWorldState(referenceBlock);
|
|
1152
1214
|
const findResult = await committedDb.getPreviousValueIndex(MerkleTreeId.NULLIFIER_TREE, nullifier.toBigInt());
|
|
1153
1215
|
if (!findResult) {
|
|
1154
1216
|
return undefined;
|
|
1155
1217
|
}
|
|
1156
1218
|
const { index, alreadyPresent } = findResult;
|
|
1157
1219
|
if (alreadyPresent) {
|
|
1158
|
-
|
|
1220
|
+
throw new Error(
|
|
1221
|
+
`Cannot prove nullifier non-inclusion: nullifier ${nullifier.toBigInt()} already exists in the tree`,
|
|
1222
|
+
);
|
|
1159
1223
|
}
|
|
1160
1224
|
const preimageData = (await committedDb.getLeafPreimage(MerkleTreeId.NULLIFIER_TREE, index))!;
|
|
1161
1225
|
|
|
@@ -1164,7 +1228,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1164
1228
|
}
|
|
1165
1229
|
|
|
1166
1230
|
async getPublicDataWitness(referenceBlock: BlockParameter, leafSlot: Fr): Promise<PublicDataWitness | undefined> {
|
|
1167
|
-
const committedDb = await this
|
|
1231
|
+
const committedDb = await this.getWorldState(referenceBlock);
|
|
1168
1232
|
const lowLeafResult = await committedDb.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot.toBigInt());
|
|
1169
1233
|
if (!lowLeafResult) {
|
|
1170
1234
|
return undefined;
|
|
@@ -1179,7 +1243,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1179
1243
|
}
|
|
1180
1244
|
|
|
1181
1245
|
public async getPublicStorageAt(referenceBlock: BlockParameter, contract: AztecAddress, slot: Fr): Promise<Fr> {
|
|
1182
|
-
const committedDb = await this
|
|
1246
|
+
const committedDb = await this.getWorldState(referenceBlock);
|
|
1183
1247
|
const leafSlot = await computePublicDataTreeLeafSlot(contract, slot);
|
|
1184
1248
|
|
|
1185
1249
|
const lowLeafResult = await committedDb.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot.toBigInt());
|
|
@@ -1319,7 +1383,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1319
1383
|
{ isSimulation, skipFeeEnforcement }: { isSimulation?: boolean; skipFeeEnforcement?: boolean } = {},
|
|
1320
1384
|
): Promise<TxValidationResult> {
|
|
1321
1385
|
const db = this.worldStateSynchronizer.getCommitted();
|
|
1322
|
-
const verifier = isSimulation ? undefined : this.
|
|
1386
|
+
const verifier = isSimulation ? undefined : this.rpcProofVerifier;
|
|
1323
1387
|
|
|
1324
1388
|
// We accept transactions if they are not expired by the next slot (checked based on the ExpirationTimestamp field)
|
|
1325
1389
|
const { ts: nextSlotTimestamp } = this.epochCache.getEpochAndSlotInNextL1Slot();
|
|
@@ -1368,7 +1432,15 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1368
1432
|
archiver.updateConfig(config);
|
|
1369
1433
|
}
|
|
1370
1434
|
if (newConfig.realProofs !== this.config.realProofs) {
|
|
1371
|
-
|
|
1435
|
+
await Promise.all([tryStop(this.peerProofVerifier), tryStop(this.rpcProofVerifier)]);
|
|
1436
|
+
if (newConfig.realProofs) {
|
|
1437
|
+
this.peerProofVerifier = await BatchChonkVerifier.new(newConfig, newConfig.bbChonkVerifyMaxBatch, 'peer');
|
|
1438
|
+
const rpcVerifier = await BBCircuitVerifier.new(newConfig);
|
|
1439
|
+
this.rpcProofVerifier = new QueuedIVCVerifier(rpcVerifier, newConfig.numConcurrentIVCVerifiers);
|
|
1440
|
+
} else {
|
|
1441
|
+
this.peerProofVerifier = new TestCircuitVerifier();
|
|
1442
|
+
this.rpcProofVerifier = new TestCircuitVerifier();
|
|
1443
|
+
}
|
|
1372
1444
|
}
|
|
1373
1445
|
|
|
1374
1446
|
this.config = newConfig;
|
|
@@ -1443,7 +1515,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1443
1515
|
return Promise.resolve();
|
|
1444
1516
|
}
|
|
1445
1517
|
|
|
1446
|
-
public async rollbackTo(targetBlock: BlockNumber, force?: boolean): Promise<void> {
|
|
1518
|
+
public async rollbackTo(targetBlock: BlockNumber, force?: boolean, resumeSync = true): Promise<void> {
|
|
1447
1519
|
const archiver = this.blockSource as Archiver;
|
|
1448
1520
|
if (!('rollbackTo' in archiver)) {
|
|
1449
1521
|
throw new Error('Archiver implementation does not support rollbacks.');
|
|
@@ -1473,9 +1545,13 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1473
1545
|
this.log.error(`Error during rollback`, err);
|
|
1474
1546
|
throw err;
|
|
1475
1547
|
} finally {
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
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
|
+
}
|
|
1479
1555
|
}
|
|
1480
1556
|
}
|
|
1481
1557
|
|
|
@@ -1492,19 +1568,12 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1492
1568
|
return Promise.resolve();
|
|
1493
1569
|
}
|
|
1494
1570
|
|
|
1495
|
-
public getSlashPayloads(): Promise<SlashPayloadRound[]> {
|
|
1496
|
-
if (!this.slasherClient) {
|
|
1497
|
-
throw new Error(`Slasher client not enabled`);
|
|
1498
|
-
}
|
|
1499
|
-
return this.slasherClient.getSlashPayloads();
|
|
1500
|
-
}
|
|
1501
|
-
|
|
1502
1571
|
public getSlashOffenses(round: bigint | 'all' | 'current'): Promise<Offense[]> {
|
|
1503
1572
|
if (!this.slasherClient) {
|
|
1504
1573
|
throw new Error(`Slasher client not enabled`);
|
|
1505
1574
|
}
|
|
1506
1575
|
if (round === 'all') {
|
|
1507
|
-
return this.slasherClient.
|
|
1576
|
+
return this.slasherClient.getOffenses();
|
|
1508
1577
|
} else {
|
|
1509
1578
|
return this.slasherClient.gatherOffensesForRound(round === 'current' ? undefined : BigInt(round));
|
|
1510
1579
|
}
|
|
@@ -1598,6 +1667,40 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1598
1667
|
this.log.info('Keystore reloaded: coinbase, feeRecipient, and attester keys updated');
|
|
1599
1668
|
}
|
|
1600
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
|
+
|
|
1601
1704
|
#getInitialHeaderHash(): Promise<BlockHash> {
|
|
1602
1705
|
if (!this.initialHeaderHashPromise) {
|
|
1603
1706
|
this.initialHeaderHashPromise = this.worldStateSynchronizer.getCommitted().getInitialHeader().hash();
|
|
@@ -1610,7 +1713,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1610
1713
|
* @param block - The block parameter (block number, block hash, or 'latest') at which to get the data.
|
|
1611
1714
|
* @returns An instance of a committed MerkleTreeOperations
|
|
1612
1715
|
*/
|
|
1613
|
-
async
|
|
1716
|
+
protected async getWorldState(block: BlockParameter) {
|
|
1614
1717
|
let blockSyncedTo: BlockNumber = BlockNumber.ZERO;
|
|
1615
1718
|
try {
|
|
1616
1719
|
// Attempt to sync the world state if necessary
|
|
@@ -1624,35 +1727,67 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1624
1727
|
return this.worldStateSynchronizer.getCommitted();
|
|
1625
1728
|
}
|
|
1626
1729
|
|
|
1730
|
+
// Get the block number, either directly from the parameter or by quering the archiver with the block hash
|
|
1731
|
+
let blockNumber: BlockNumber;
|
|
1627
1732
|
if (BlockHash.isBlockHash(block)) {
|
|
1628
1733
|
const initialBlockHash = await this.#getInitialHeaderHash();
|
|
1629
1734
|
if (block.equals(initialBlockHash)) {
|
|
1630
|
-
// Block
|
|
1631
|
-
|
|
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();
|
|
1632
1747
|
}
|
|
1748
|
+
} else {
|
|
1749
|
+
blockNumber = block as BlockNumber;
|
|
1750
|
+
}
|
|
1633
1751
|
|
|
1634
|
-
|
|
1635
|
-
|
|
1752
|
+
// Check it's within world state sync range
|
|
1753
|
+
if (blockNumber > blockSyncedTo) {
|
|
1754
|
+
throw new Error(`Queried block ${block} not yet synced by the node (node is synced upto ${blockSyncedTo}).`);
|
|
1755
|
+
}
|
|
1756
|
+
this.log.debug(`Using snapshot for block ${blockNumber}, world state synced upto ${blockSyncedTo}`);
|
|
1757
|
+
|
|
1758
|
+
const snapshot = this.worldStateSynchronizer.getSnapshot(blockNumber);
|
|
1759
|
+
|
|
1760
|
+
// Double-check world-state synced to the same block hash as was requested
|
|
1761
|
+
if (BlockHash.isBlockHash(block)) {
|
|
1762
|
+
const blockHash = await snapshot.getLeafValue(MerkleTreeId.ARCHIVE, BigInt(blockNumber));
|
|
1763
|
+
if (!blockHash || !block.equals(blockHash)) {
|
|
1764
|
+
const initialBlockHash = await this.#getInitialHeaderHash();
|
|
1636
1765
|
throw new Error(
|
|
1637
|
-
`Block hash ${block.toString()} not found
|
|
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.`,
|
|
1638
1767
|
);
|
|
1639
1768
|
}
|
|
1640
|
-
const blockNumber = header.getBlockNumber();
|
|
1641
|
-
this.log.debug(`Using snapshot for block ${blockNumber}, world state synced upto ${blockSyncedTo}`);
|
|
1642
|
-
return this.worldStateSynchronizer.getSnapshot(blockNumber);
|
|
1643
1769
|
}
|
|
1644
1770
|
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
const blockNumber = block as BlockNumber;
|
|
1771
|
+
return snapshot;
|
|
1772
|
+
}
|
|
1648
1773
|
|
|
1649
|
-
|
|
1650
|
-
|
|
1774
|
+
/** Resolves a block parameter to a block number. */
|
|
1775
|
+
protected async resolveBlockNumber(block: BlockParameter): Promise<BlockNumber> {
|
|
1776
|
+
if (block === 'latest') {
|
|
1777
|
+
return BlockNumber(await this.blockSource.getBlockNumber());
|
|
1778
|
+
}
|
|
1779
|
+
if (BlockHash.isBlockHash(block)) {
|
|
1780
|
+
const initialBlockHash = await this.#getInitialHeaderHash();
|
|
1781
|
+
if (block.equals(initialBlockHash)) {
|
|
1782
|
+
return BlockNumber.ZERO;
|
|
1651
1783
|
}
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1784
|
+
const header = await this.blockSource.getBlockHeaderByHash(block);
|
|
1785
|
+
if (!header) {
|
|
1786
|
+
throw new Error(`Block hash ${block.toString()} not found.`);
|
|
1787
|
+
}
|
|
1788
|
+
return header.getBlockNumber();
|
|
1655
1789
|
}
|
|
1790
|
+
return block as BlockNumber;
|
|
1656
1791
|
}
|
|
1657
1792
|
|
|
1658
1793
|
/**
|