@aztec/aztec-node 0.0.1-commit.96dac018d → 0.0.1-commit.9badcec54
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/config.d.ts +2 -2
- package/dest/aztec-node/config.d.ts.map +1 -1
- package/dest/aztec-node/config.js +1 -1
- package/dest/aztec-node/server.d.ts +32 -34
- package/dest/aztec-node/server.d.ts.map +1 -1
- package/dest/aztec-node/server.js +308 -176
- package/dest/sentinel/factory.d.ts +2 -2
- package/dest/sentinel/factory.d.ts.map +1 -1
- package/dest/sentinel/sentinel.js +9 -9
- package/package.json +26 -27
- package/src/aztec-node/config.ts +1 -1
- package/src/aztec-node/server.ts +344 -195
- package/src/sentinel/factory.ts +1 -1
- package/src/sentinel/sentinel.ts +9 -9
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,12 +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 {
|
|
83
|
-
import type {
|
|
84
|
-
import type { NullifierLeafPreimage, PublicDataTreeLeaf, PublicDataTreeLeafPreimage } from '@aztec/stdlib/trees';
|
|
91
|
+
import type { Offense } from '@aztec/stdlib/slashing';
|
|
92
|
+
import type { NullifierLeafPreimage, PublicDataTreeLeafPreimage } from '@aztec/stdlib/trees';
|
|
85
93
|
import { MerkleTreeId, NullifierMembershipWitness, PublicDataWitness } from '@aztec/stdlib/trees';
|
|
86
94
|
import {
|
|
87
95
|
type BlockHeader,
|
|
96
|
+
type FeeProvider,
|
|
88
97
|
type GlobalVariableBuilder as GlobalVariableBuilderInterface,
|
|
89
98
|
type IndexedTxEffect,
|
|
90
99
|
PublicSimulationOutput,
|
|
@@ -96,6 +105,7 @@ import {
|
|
|
96
105
|
} from '@aztec/stdlib/tx';
|
|
97
106
|
import { getPackageVersion } from '@aztec/stdlib/update-checker';
|
|
98
107
|
import type { SingleValidatorStats, ValidatorsStats } from '@aztec/stdlib/validators';
|
|
108
|
+
import type { GenesisData } from '@aztec/stdlib/world-state';
|
|
99
109
|
import {
|
|
100
110
|
Attributes,
|
|
101
111
|
type TelemetryClient,
|
|
@@ -109,12 +119,13 @@ import {
|
|
|
109
119
|
FullNodeCheckpointsBuilder,
|
|
110
120
|
NodeKeystoreAdapter,
|
|
111
121
|
ValidatorClient,
|
|
112
|
-
|
|
122
|
+
createProposalHandler,
|
|
113
123
|
createValidatorClient,
|
|
114
124
|
} from '@aztec/validator-client';
|
|
125
|
+
import type { SlashingProtectionDatabase } from '@aztec/validator-ha-signer/types';
|
|
115
126
|
import { createWorldStateSynchronizer } from '@aztec/world-state';
|
|
116
127
|
|
|
117
|
-
import { createPublicClient
|
|
128
|
+
import { createPublicClient } from 'viem';
|
|
118
129
|
|
|
119
130
|
import { createSentinel } from '../sentinel/factory.js';
|
|
120
131
|
import { Sentinel } from '../sentinel/sentinel.js';
|
|
@@ -124,7 +135,7 @@ import { NodeMetrics } from './node_metrics.js';
|
|
|
124
135
|
/**
|
|
125
136
|
* The aztec node.
|
|
126
137
|
*/
|
|
127
|
-
export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
138
|
+
export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDebug, Traceable {
|
|
128
139
|
private metrics: NodeMetrics;
|
|
129
140
|
private initialHeaderHashPromise: Promise<BlockHash> | undefined = undefined;
|
|
130
141
|
|
|
@@ -149,9 +160,11 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
149
160
|
protected readonly l1ChainId: number,
|
|
150
161
|
protected readonly version: number,
|
|
151
162
|
protected readonly globalVariableBuilder: GlobalVariableBuilderInterface,
|
|
163
|
+
protected readonly feeProvider: FeeProvider,
|
|
152
164
|
protected readonly epochCache: EpochCacheInterface,
|
|
153
165
|
protected readonly packageVersion: string,
|
|
154
|
-
private
|
|
166
|
+
private peerProofVerifier: ClientProtocolCircuitVerifier,
|
|
167
|
+
private rpcProofVerifier: ClientProtocolCircuitVerifier,
|
|
155
168
|
private telemetry: TelemetryClient = getTelemetryClient(),
|
|
156
169
|
private log = createLogger('node'),
|
|
157
170
|
private blobClient?: BlobClientInterface,
|
|
@@ -173,6 +186,11 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
173
186
|
}
|
|
174
187
|
}
|
|
175
188
|
|
|
189
|
+
/** @internal Exposed for testing — returns the RPC proof verifier. */
|
|
190
|
+
public getProofVerifier(): ClientProtocolCircuitVerifier {
|
|
191
|
+
return this.rpcProofVerifier;
|
|
192
|
+
}
|
|
193
|
+
|
|
176
194
|
public async getWorldStateSyncStatus(): Promise<WorldStateSyncStatus> {
|
|
177
195
|
const status = await this.worldStateSynchronizer.status();
|
|
178
196
|
return status.syncSummary;
|
|
@@ -194,11 +212,12 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
194
212
|
logger?: Logger;
|
|
195
213
|
publisher?: SequencerPublisher;
|
|
196
214
|
dateProvider?: DateProvider;
|
|
197
|
-
p2pClientDeps?: P2PClientDeps
|
|
215
|
+
p2pClientDeps?: P2PClientDeps;
|
|
198
216
|
proverNodeDeps?: Partial<ProverNodeDeps>;
|
|
217
|
+
slashingProtectionDb?: SlashingProtectionDatabase;
|
|
199
218
|
} = {},
|
|
200
219
|
options: {
|
|
201
|
-
|
|
220
|
+
genesis?: GenesisData;
|
|
202
221
|
dontStartSequencer?: boolean;
|
|
203
222
|
dontStartProverNode?: boolean;
|
|
204
223
|
} = {},
|
|
@@ -258,7 +277,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
258
277
|
|
|
259
278
|
const publicClient = createPublicClient({
|
|
260
279
|
chain: ethereumChain.chainInfo,
|
|
261
|
-
transport:
|
|
280
|
+
transport: makeL1HttpTransport(config.l1RpcUrls, { timeout: config.l1HttpTimeoutMS }),
|
|
262
281
|
pollingInterval: config.viemPollingIntervalMS,
|
|
263
282
|
});
|
|
264
283
|
|
|
@@ -272,10 +291,11 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
272
291
|
config.l1Contracts = { ...config.l1Contracts, ...l1ContractsAddresses };
|
|
273
292
|
|
|
274
293
|
const rollupContract = new RollupContract(publicClient, config.l1Contracts.rollupAddress.toString());
|
|
275
|
-
const [l1GenesisTime, slotDuration, rollupVersionFromRollup] = await Promise.all([
|
|
294
|
+
const [l1GenesisTime, slotDuration, rollupVersionFromRollup, rollupManaLimit] = await Promise.all([
|
|
276
295
|
rollupContract.getL1GenesisTime(),
|
|
277
296
|
rollupContract.getSlotDuration(),
|
|
278
297
|
rollupContract.getVersion(),
|
|
298
|
+
rollupContract.getManaLimit().then(Number),
|
|
279
299
|
] as const);
|
|
280
300
|
|
|
281
301
|
config.rollupVersion ??= Number(rollupVersionFromRollup);
|
|
@@ -300,16 +320,18 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
300
320
|
);
|
|
301
321
|
|
|
302
322
|
// now create the merkle trees and the world state synchronizer
|
|
303
|
-
const worldStateSynchronizer = await createWorldStateSynchronizer(
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
323
|
+
const worldStateSynchronizer = await createWorldStateSynchronizer(config, archiver, options.genesis, telemetry);
|
|
324
|
+
const useRealVerifiers = config.realProofs || config.debugForceTxProofVerification;
|
|
325
|
+
let peerProofVerifier: ClientProtocolCircuitVerifier;
|
|
326
|
+
let rpcProofVerifier: ClientProtocolCircuitVerifier;
|
|
327
|
+
if (useRealVerifiers) {
|
|
328
|
+
peerProofVerifier = await BatchChonkVerifier.new(config, config.bbChonkVerifyMaxBatch, 'peer');
|
|
329
|
+
const rpcVerifier = await BBCircuitVerifier.new(config);
|
|
330
|
+
rpcProofVerifier = new QueuedIVCVerifier(rpcVerifier, config.numConcurrentIVCVerifiers);
|
|
331
|
+
} else {
|
|
332
|
+
peerProofVerifier = new TestCircuitVerifier(config.proverTestVerificationDelayMs);
|
|
333
|
+
rpcProofVerifier = new TestCircuitVerifier(config.proverTestVerificationDelayMs);
|
|
334
|
+
}
|
|
313
335
|
|
|
314
336
|
let debugLogStore: DebugLogStore;
|
|
315
337
|
if (!config.realProofs) {
|
|
@@ -323,14 +345,16 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
323
345
|
debugLogStore = new NullDebugLogStore();
|
|
324
346
|
}
|
|
325
347
|
|
|
326
|
-
const
|
|
348
|
+
const proverOnly = config.enableProverNode && config.disableValidator;
|
|
349
|
+
if (proverOnly) {
|
|
350
|
+
log.info('Starting in prover-only mode: skipping validator, sequencer, sentinel, and slasher subsystems');
|
|
351
|
+
}
|
|
327
352
|
|
|
328
353
|
// create the tx pool and the p2p client, which will need the l2 block source
|
|
329
354
|
const p2pClient = await createP2PClient(
|
|
330
|
-
P2PClientType.Full,
|
|
331
355
|
config,
|
|
332
356
|
archiver,
|
|
333
|
-
|
|
357
|
+
peerProofVerifier,
|
|
334
358
|
worldStateSynchronizer,
|
|
335
359
|
epochCache,
|
|
336
360
|
packageVersion,
|
|
@@ -339,59 +363,77 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
339
363
|
deps.p2pClientDeps,
|
|
340
364
|
);
|
|
341
365
|
|
|
342
|
-
// We
|
|
343
|
-
|
|
366
|
+
// We'll accumulate sentinel watchers here
|
|
367
|
+
const watchers: Watcher[] = [];
|
|
344
368
|
|
|
345
|
-
// Create FullNodeCheckpointsBuilder for
|
|
369
|
+
// Create FullNodeCheckpointsBuilder for block proposal handling and tx validation.
|
|
370
|
+
// Override maxTxsPerCheckpoint with the validator-specific limit if set.
|
|
346
371
|
const validatorCheckpointsBuilder = new FullNodeCheckpointsBuilder(
|
|
347
|
-
{
|
|
372
|
+
{
|
|
373
|
+
...config,
|
|
374
|
+
l1GenesisTime,
|
|
375
|
+
slotDuration: Number(slotDuration),
|
|
376
|
+
rollupManaLimit,
|
|
377
|
+
maxTxsPerCheckpoint: config.validateMaxTxsPerCheckpoint,
|
|
378
|
+
},
|
|
348
379
|
worldStateSynchronizer,
|
|
349
380
|
archiver,
|
|
350
381
|
dateProvider,
|
|
351
382
|
telemetry,
|
|
352
383
|
);
|
|
353
384
|
|
|
354
|
-
|
|
355
|
-
const watchers: Watcher[] = [];
|
|
385
|
+
let validatorClient: ValidatorClient | undefined;
|
|
356
386
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
387
|
+
if (!proverOnly) {
|
|
388
|
+
// Create validator client if required
|
|
389
|
+
validatorClient = await createValidatorClient(config, {
|
|
390
|
+
checkpointsBuilder: validatorCheckpointsBuilder,
|
|
391
|
+
worldState: worldStateSynchronizer,
|
|
392
|
+
p2pClient,
|
|
393
|
+
telemetry,
|
|
394
|
+
dateProvider,
|
|
395
|
+
epochCache,
|
|
396
|
+
blockSource: archiver,
|
|
397
|
+
l1ToL2MessageSource: archiver,
|
|
398
|
+
keyStoreManager,
|
|
399
|
+
blobClient,
|
|
400
|
+
slashingProtectionDb: deps.slashingProtectionDb,
|
|
401
|
+
});
|
|
370
402
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
403
|
+
// If we have a validator client, register it as a source of offenses for the slasher,
|
|
404
|
+
// and have it register callbacks on the p2p client *before* we start it, otherwise messages
|
|
405
|
+
// like attestations or auths will fail.
|
|
406
|
+
if (validatorClient) {
|
|
407
|
+
watchers.push(validatorClient);
|
|
408
|
+
|
|
409
|
+
const vc = validatorClient;
|
|
410
|
+
const getValidatorAddresses = () => vc.getValidatorAddresses().map(a => a.toString());
|
|
411
|
+
validatorClient.getProposalHandler().register(p2pClient, true, archiver, getValidatorAddresses);
|
|
412
|
+
|
|
413
|
+
if (!options.dontStartSequencer) {
|
|
414
|
+
await validatorClient.registerHandlers();
|
|
415
|
+
}
|
|
378
416
|
}
|
|
379
417
|
}
|
|
380
418
|
|
|
381
|
-
// If there's no validator client
|
|
382
|
-
//
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
419
|
+
// If there's no validator client, create a ProposalHandler to handle block and checkpoint proposals
|
|
420
|
+
// for monitoring or reexecution. Reexecution (default) allows us to follow the pending chain,
|
|
421
|
+
// while non-reexecution is used for validating the proposals and collecting their txs.
|
|
422
|
+
// Checkpoint proposals rebuild blobs if the blob client can upload blobs.
|
|
423
|
+
if (!validatorClient) {
|
|
424
|
+
const reexecute = !!config.alwaysReexecuteBlockProposals;
|
|
425
|
+
log.info(`Setting up proposal handler` + (reexecute ? ' with reexecution of proposals' : ''));
|
|
426
|
+
createProposalHandler(config, {
|
|
386
427
|
checkpointsBuilder: validatorCheckpointsBuilder,
|
|
387
428
|
worldState: worldStateSynchronizer,
|
|
388
429
|
epochCache,
|
|
389
430
|
blockSource: archiver,
|
|
390
431
|
l1ToL2MessageSource: archiver,
|
|
391
432
|
p2pClient,
|
|
433
|
+
blobClient,
|
|
392
434
|
dateProvider,
|
|
393
435
|
telemetry,
|
|
394
|
-
}).
|
|
436
|
+
}).register(p2pClient, reexecute, archiver);
|
|
395
437
|
}
|
|
396
438
|
|
|
397
439
|
// Start world state and wait for it to sync to the archiver.
|
|
@@ -400,36 +442,39 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
400
442
|
// Start p2p. Note that it depends on world state to be running.
|
|
401
443
|
await p2pClient.start();
|
|
402
444
|
|
|
403
|
-
|
|
404
|
-
if (validatorsSentinel && config.slashInactivityPenalty > 0n) {
|
|
405
|
-
watchers.push(validatorsSentinel);
|
|
406
|
-
}
|
|
407
|
-
|
|
445
|
+
let validatorsSentinel: Awaited<ReturnType<typeof createSentinel>> | undefined;
|
|
408
446
|
let epochPruneWatcher: EpochPruneWatcher | undefined;
|
|
409
|
-
if (config.slashPrunePenalty > 0n || config.slashDataWithholdingPenalty > 0n) {
|
|
410
|
-
epochPruneWatcher = new EpochPruneWatcher(
|
|
411
|
-
archiver,
|
|
412
|
-
archiver,
|
|
413
|
-
epochCache,
|
|
414
|
-
p2pClient.getTxProvider(),
|
|
415
|
-
validatorCheckpointsBuilder,
|
|
416
|
-
config,
|
|
417
|
-
);
|
|
418
|
-
watchers.push(epochPruneWatcher);
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
// We assume we want to slash for invalid attestations unless all max penalties are set to 0
|
|
422
447
|
let attestationsBlockWatcher: AttestationsBlockWatcher | undefined;
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
448
|
+
|
|
449
|
+
if (!proverOnly) {
|
|
450
|
+
validatorsSentinel = await createSentinel(epochCache, archiver, p2pClient, config);
|
|
451
|
+
if (validatorsSentinel && config.slashInactivityPenalty > 0n) {
|
|
452
|
+
watchers.push(validatorsSentinel);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
if (config.slashPrunePenalty > 0n || config.slashDataWithholdingPenalty > 0n) {
|
|
456
|
+
epochPruneWatcher = new EpochPruneWatcher(
|
|
457
|
+
archiver,
|
|
458
|
+
archiver,
|
|
459
|
+
epochCache,
|
|
460
|
+
p2pClient.getTxProvider(),
|
|
461
|
+
validatorCheckpointsBuilder,
|
|
462
|
+
config,
|
|
463
|
+
);
|
|
464
|
+
watchers.push(epochPruneWatcher);
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// We assume we want to slash for invalid attestations unless all max penalties are set to 0
|
|
468
|
+
if (config.slashProposeInvalidAttestationsPenalty > 0n || config.slashAttestDescendantOfInvalidPenalty > 0n) {
|
|
469
|
+
attestationsBlockWatcher = new AttestationsBlockWatcher(archiver, epochCache, config);
|
|
470
|
+
watchers.push(attestationsBlockWatcher);
|
|
471
|
+
}
|
|
426
472
|
}
|
|
427
473
|
|
|
428
474
|
// Start p2p-related services once the archiver has completed sync
|
|
429
475
|
void archiver
|
|
430
476
|
.waitForInitialSync()
|
|
431
477
|
.then(async () => {
|
|
432
|
-
await p2pClient.start();
|
|
433
478
|
await validatorsSentinel?.start();
|
|
434
479
|
await epochPruneWatcher?.start();
|
|
435
480
|
await attestationsBlockWatcher?.start();
|
|
@@ -437,6 +482,17 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
437
482
|
})
|
|
438
483
|
.catch(err => log.error('Failed to start p2p services after archiver sync', err));
|
|
439
484
|
|
|
485
|
+
const globalVariableBuilderConfig = {
|
|
486
|
+
l1Contracts: config.l1Contracts,
|
|
487
|
+
ethereumSlotDuration: config.ethereumSlotDuration,
|
|
488
|
+
rollupVersion: BigInt(config.rollupVersion),
|
|
489
|
+
l1GenesisTime,
|
|
490
|
+
slotDuration: Number(slotDuration),
|
|
491
|
+
};
|
|
492
|
+
|
|
493
|
+
const globalVariableBuilder = new GlobalVariableBuilder(dateProvider, publicClient, globalVariableBuilderConfig);
|
|
494
|
+
const feeProvider = new FeeProviderImpl(dateProvider, publicClient, globalVariableBuilderConfig);
|
|
495
|
+
|
|
440
496
|
// Validator enabled, create/start relevant service
|
|
441
497
|
let sequencer: SequencerClient | undefined;
|
|
442
498
|
let slasherClient: SlasherClientInterface | undefined;
|
|
@@ -474,9 +530,22 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
474
530
|
{ telemetry, logger: log.createChild('l1-tx-utils'), dateProvider, kzg: Blob.getViemKzgInstance() },
|
|
475
531
|
);
|
|
476
532
|
|
|
533
|
+
// Create a funder L1TxUtils from the keystore funding account (if configured)
|
|
534
|
+
const fundingSigner = keyStoreManager?.createFundingSigner();
|
|
535
|
+
let funderL1TxUtils: L1TxUtils | undefined;
|
|
536
|
+
if (fundingSigner) {
|
|
537
|
+
const [funder] = await createL1TxUtilsFromSigners(
|
|
538
|
+
publicClient,
|
|
539
|
+
[fundingSigner],
|
|
540
|
+
{ ...config, scope: 'sequencer' },
|
|
541
|
+
{ telemetry, logger: log.createChild('l1-tx-utils:funder'), dateProvider },
|
|
542
|
+
);
|
|
543
|
+
funderL1TxUtils = funder;
|
|
544
|
+
}
|
|
545
|
+
|
|
477
546
|
// Create and start the sequencer client
|
|
478
547
|
const checkpointsBuilder = new CheckpointsBuilder(
|
|
479
|
-
{ ...config, l1GenesisTime, slotDuration: Number(slotDuration) },
|
|
548
|
+
{ ...config, l1GenesisTime, slotDuration: Number(slotDuration), rollupManaLimit },
|
|
480
549
|
worldStateSynchronizer,
|
|
481
550
|
archiver,
|
|
482
551
|
dateProvider,
|
|
@@ -488,6 +557,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
488
557
|
...deps,
|
|
489
558
|
epochCache,
|
|
490
559
|
l1TxUtils,
|
|
560
|
+
funderL1TxUtils,
|
|
491
561
|
validatorClient,
|
|
492
562
|
p2pClient,
|
|
493
563
|
worldStateSynchronizer,
|
|
@@ -499,6 +569,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
499
569
|
dateProvider,
|
|
500
570
|
blobClient,
|
|
501
571
|
nodeKeyStore: keyStoreManager!,
|
|
572
|
+
globalVariableBuilder,
|
|
502
573
|
});
|
|
503
574
|
}
|
|
504
575
|
|
|
@@ -532,13 +603,6 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
532
603
|
}
|
|
533
604
|
}
|
|
534
605
|
|
|
535
|
-
const globalVariableBuilder = new GlobalVariableBuilder({
|
|
536
|
-
...config,
|
|
537
|
-
rollupVersion: BigInt(config.rollupVersion),
|
|
538
|
-
l1GenesisTime,
|
|
539
|
-
slotDuration: Number(slotDuration),
|
|
540
|
-
});
|
|
541
|
-
|
|
542
606
|
const node = new AztecNodeService(
|
|
543
607
|
config,
|
|
544
608
|
p2pClient,
|
|
@@ -555,9 +619,11 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
555
619
|
ethereumChain.chainInfo.id,
|
|
556
620
|
config.rollupVersion,
|
|
557
621
|
globalVariableBuilder,
|
|
622
|
+
feeProvider,
|
|
558
623
|
epochCache,
|
|
559
624
|
packageVersion,
|
|
560
|
-
|
|
625
|
+
peerProofVerifier,
|
|
626
|
+
rpcProofVerifier,
|
|
561
627
|
telemetry,
|
|
562
628
|
log,
|
|
563
629
|
blobClient,
|
|
@@ -607,7 +673,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
607
673
|
}
|
|
608
674
|
|
|
609
675
|
public async getAllowedPublicSetup(): Promise<AllowedElement[]> {
|
|
610
|
-
return this.config.
|
|
676
|
+
return [...(await getDefaultAllowedSetupFunctions()), ...(this.config.txPublicSetupAllowListExtend ?? [])];
|
|
611
677
|
}
|
|
612
678
|
|
|
613
679
|
/**
|
|
@@ -702,12 +768,17 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
702
768
|
return (await this.blockSource.getCheckpointedBlocks(from, limit)) ?? [];
|
|
703
769
|
}
|
|
704
770
|
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
771
|
+
public getCheckpointsDataForEpoch(epochNumber: EpochNumber) {
|
|
772
|
+
return this.blockSource.getCheckpointsDataForEpoch(epochNumber);
|
|
773
|
+
}
|
|
774
|
+
|
|
709
775
|
public async getCurrentMinFees(): Promise<GasFees> {
|
|
710
|
-
return await this.
|
|
776
|
+
return await this.feeProvider.getCurrentMinFees();
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
/** Returns predicted min fees for the current slot and next N slots. */
|
|
780
|
+
public async getPredictedMinFees(manaUsage?: ManaUsageEstimate): Promise<GasFees[]> {
|
|
781
|
+
return await this.feeProvider.getPredictedMinFees(manaUsage);
|
|
711
782
|
}
|
|
712
783
|
|
|
713
784
|
public async getMaxPriorityFees(): Promise<GasFees> {
|
|
@@ -734,6 +805,10 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
734
805
|
return await this.blockSource.getCheckpointedL2BlockNumber();
|
|
735
806
|
}
|
|
736
807
|
|
|
808
|
+
public getCheckpointNumber(): Promise<CheckpointNumber> {
|
|
809
|
+
return this.blockSource.getCheckpointNumber();
|
|
810
|
+
}
|
|
811
|
+
|
|
737
812
|
/**
|
|
738
813
|
* Method to fetch the version of the package.
|
|
739
814
|
* @returns The node package version
|
|
@@ -771,18 +846,22 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
771
846
|
page?: number,
|
|
772
847
|
referenceBlock?: BlockHash,
|
|
773
848
|
): Promise<TxScopedL2Log[][]> {
|
|
849
|
+
let upToBlockNumber: BlockNumber | undefined;
|
|
774
850
|
if (referenceBlock) {
|
|
775
851
|
const initialBlockHash = await this.#getInitialHeaderHash();
|
|
776
|
-
if (
|
|
852
|
+
if (referenceBlock.equals(initialBlockHash)) {
|
|
853
|
+
upToBlockNumber = BlockNumber(0);
|
|
854
|
+
} else {
|
|
777
855
|
const header = await this.blockSource.getBlockHeaderByHash(referenceBlock);
|
|
778
856
|
if (!header) {
|
|
779
857
|
throw new Error(
|
|
780
858
|
`Block ${referenceBlock.toString()} not found in the node. This might indicate a reorg has occurred.`,
|
|
781
859
|
);
|
|
782
860
|
}
|
|
861
|
+
upToBlockNumber = header.globalVariables.blockNumber;
|
|
783
862
|
}
|
|
784
863
|
}
|
|
785
|
-
return this.logsSource.getPrivateLogsByTags(tags, page);
|
|
864
|
+
return this.logsSource.getPrivateLogsByTags(tags, page, upToBlockNumber);
|
|
786
865
|
}
|
|
787
866
|
|
|
788
867
|
public async getPublicLogsByTagsFromContract(
|
|
@@ -791,18 +870,22 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
791
870
|
page?: number,
|
|
792
871
|
referenceBlock?: BlockHash,
|
|
793
872
|
): Promise<TxScopedL2Log[][]> {
|
|
873
|
+
let upToBlockNumber: BlockNumber | undefined;
|
|
794
874
|
if (referenceBlock) {
|
|
795
875
|
const initialBlockHash = await this.#getInitialHeaderHash();
|
|
796
|
-
if (
|
|
876
|
+
if (referenceBlock.equals(initialBlockHash)) {
|
|
877
|
+
upToBlockNumber = BlockNumber(0);
|
|
878
|
+
} else {
|
|
797
879
|
const header = await this.blockSource.getBlockHeaderByHash(referenceBlock);
|
|
798
880
|
if (!header) {
|
|
799
881
|
throw new Error(
|
|
800
882
|
`Block ${referenceBlock.toString()} not found in the node. This might indicate a reorg has occurred.`,
|
|
801
883
|
);
|
|
802
884
|
}
|
|
885
|
+
upToBlockNumber = header.globalVariables.blockNumber;
|
|
803
886
|
}
|
|
804
887
|
}
|
|
805
|
-
return this.logsSource.getPublicLogsByTagsFromContract(contractAddress, tags, page);
|
|
888
|
+
return this.logsSource.getPublicLogsByTagsFromContract(contractAddress, tags, page, upToBlockNumber);
|
|
806
889
|
}
|
|
807
890
|
|
|
808
891
|
/**
|
|
@@ -844,8 +927,9 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
844
927
|
}
|
|
845
928
|
|
|
846
929
|
await this.p2pClient!.sendTx(tx);
|
|
847
|
-
|
|
848
|
-
this.
|
|
930
|
+
const duration = timer.ms();
|
|
931
|
+
this.metrics.receivedTx(duration, true);
|
|
932
|
+
this.log.info(`Received tx ${txHash} in ${duration}ms`, { txHash });
|
|
849
933
|
}
|
|
850
934
|
|
|
851
935
|
public async getTxReceipt(txHash: TxHash): Promise<TxReceipt> {
|
|
@@ -887,7 +971,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
887
971
|
await tryStop(this.validatorsSentinel);
|
|
888
972
|
await tryStop(this.epochPruneWatcher);
|
|
889
973
|
await tryStop(this.slasherClient);
|
|
890
|
-
await tryStop(this.
|
|
974
|
+
await Promise.all([tryStop(this.peerProofVerifier), tryStop(this.rpcProofVerifier)]);
|
|
891
975
|
await tryStop(this.sequencer);
|
|
892
976
|
await tryStop(this.proverNode);
|
|
893
977
|
await tryStop(this.p2pClient);
|
|
@@ -943,59 +1027,65 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
943
1027
|
treeId: MerkleTreeId,
|
|
944
1028
|
leafValues: Fr[],
|
|
945
1029
|
): Promise<(DataInBlock<bigint> | undefined)[]> {
|
|
946
|
-
const committedDb = await this
|
|
1030
|
+
const committedDb = await this.getWorldState(referenceBlock);
|
|
947
1031
|
const maybeIndices = await committedDb.findLeafIndices(
|
|
948
1032
|
treeId,
|
|
949
1033
|
leafValues.map(x => x.toBuffer()),
|
|
950
1034
|
);
|
|
951
|
-
//
|
|
952
|
-
const
|
|
1035
|
+
// Filter out undefined values to query block numbers only for found leaves
|
|
1036
|
+
const definedIndices = maybeIndices.filter(x => x !== undefined);
|
|
953
1037
|
|
|
954
|
-
// Now we find the block numbers for the indices
|
|
955
|
-
const blockNumbers = await committedDb.getBlockNumbersForLeafIndices(treeId,
|
|
1038
|
+
// Now we find the block numbers for the defined indices
|
|
1039
|
+
const blockNumbers = await committedDb.getBlockNumbersForLeafIndices(treeId, definedIndices);
|
|
956
1040
|
|
|
957
|
-
//
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
1041
|
+
// Build a map from leaf index to block number
|
|
1042
|
+
const indexToBlockNumber = new Map<bigint, BlockNumber>();
|
|
1043
|
+
for (let i = 0; i < definedIndices.length; i++) {
|
|
1044
|
+
const blockNumber = blockNumbers[i];
|
|
1045
|
+
if (blockNumber === undefined) {
|
|
1046
|
+
throw new Error(
|
|
1047
|
+
`Block number is undefined for leaf index ${definedIndices[i]} in tree ${MerkleTreeId[treeId]}`,
|
|
1048
|
+
);
|
|
961
1049
|
}
|
|
1050
|
+
indexToBlockNumber.set(definedIndices[i], blockNumber);
|
|
962
1051
|
}
|
|
963
1052
|
|
|
964
1053
|
// Get unique block numbers in order to optimize num calls to getLeafValue function.
|
|
965
|
-
const uniqueBlockNumbers = [...new Set(
|
|
1054
|
+
const uniqueBlockNumbers = [...new Set(indexToBlockNumber.values())];
|
|
966
1055
|
|
|
967
|
-
// Now we obtain the block hashes from the archive tree
|
|
968
|
-
// (note that block number corresponds to the leaf index in the archive tree).
|
|
1056
|
+
// Now we obtain the block hashes from the archive tree (block number = leaf index in archive tree).
|
|
969
1057
|
const blockHashes = await Promise.all(
|
|
970
1058
|
uniqueBlockNumbers.map(blockNumber => {
|
|
971
1059
|
return committedDb.getLeafValue(MerkleTreeId.ARCHIVE, BigInt(blockNumber));
|
|
972
1060
|
}),
|
|
973
1061
|
);
|
|
974
1062
|
|
|
975
|
-
//
|
|
1063
|
+
// Build a map from block number to block hash
|
|
1064
|
+
const blockNumberToHash = new Map<BlockNumber, BlockHash>();
|
|
976
1065
|
for (let i = 0; i < uniqueBlockNumbers.length; i++) {
|
|
977
|
-
|
|
1066
|
+
const blockHash = blockHashes[i];
|
|
1067
|
+
if (blockHash === undefined) {
|
|
978
1068
|
throw new Error(`Block hash is undefined for block number ${uniqueBlockNumbers[i]}`);
|
|
979
1069
|
}
|
|
1070
|
+
blockNumberToHash.set(uniqueBlockNumbers[i], blockHash);
|
|
980
1071
|
}
|
|
981
1072
|
|
|
982
1073
|
// Create DataInBlock objects by combining indices, blockNumbers and blockHashes and return them.
|
|
983
|
-
return maybeIndices.map(
|
|
1074
|
+
return maybeIndices.map(index => {
|
|
984
1075
|
if (index === undefined) {
|
|
985
1076
|
return undefined;
|
|
986
1077
|
}
|
|
987
|
-
const blockNumber =
|
|
1078
|
+
const blockNumber = indexToBlockNumber.get(index);
|
|
988
1079
|
if (blockNumber === undefined) {
|
|
989
|
-
|
|
1080
|
+
throw new Error(`Block number not found for leaf index ${index} in tree ${MerkleTreeId[treeId]}`);
|
|
990
1081
|
}
|
|
991
|
-
const
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
return undefined;
|
|
1082
|
+
const l2BlockHash = blockNumberToHash.get(blockNumber);
|
|
1083
|
+
if (l2BlockHash === undefined) {
|
|
1084
|
+
throw new Error(`Block hash not found for block number ${blockNumber}`);
|
|
995
1085
|
}
|
|
996
1086
|
return {
|
|
997
|
-
l2BlockNumber:
|
|
998
|
-
l2BlockHash
|
|
1087
|
+
l2BlockNumber: blockNumber,
|
|
1088
|
+
l2BlockHash,
|
|
999
1089
|
data: index,
|
|
1000
1090
|
};
|
|
1001
1091
|
});
|
|
@@ -1005,7 +1095,15 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1005
1095
|
referenceBlock: BlockParameter,
|
|
1006
1096
|
blockHash: BlockHash,
|
|
1007
1097
|
): Promise<MembershipWitness<typeof ARCHIVE_HEIGHT> | undefined> {
|
|
1008
|
-
|
|
1098
|
+
// The Noir circuit checks the archive membership proof against `anchor_block_header.last_archive.root`,
|
|
1099
|
+
// which is the archive tree root BEFORE the anchor block was added (i.e. the state after block N-1).
|
|
1100
|
+
// So we need the world state at block N-1, not block N, to produce a sibling path matching that root.
|
|
1101
|
+
const referenceBlockNumber = await this.resolveBlockNumber(referenceBlock);
|
|
1102
|
+
if (referenceBlockNumber === BlockNumber.ZERO) {
|
|
1103
|
+
// Block 0 (the initial block) has an empty archive, so no membership witness can exist.
|
|
1104
|
+
return undefined;
|
|
1105
|
+
}
|
|
1106
|
+
const committedDb = await this.getWorldState(BlockNumber(referenceBlockNumber - 1));
|
|
1009
1107
|
const [pathAndIndex] = await committedDb.findSiblingPaths<MerkleTreeId.ARCHIVE>(MerkleTreeId.ARCHIVE, [blockHash]);
|
|
1010
1108
|
return pathAndIndex === undefined
|
|
1011
1109
|
? undefined
|
|
@@ -1016,7 +1114,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1016
1114
|
referenceBlock: BlockParameter,
|
|
1017
1115
|
noteHash: Fr,
|
|
1018
1116
|
): Promise<MembershipWitness<typeof NOTE_HASH_TREE_HEIGHT> | undefined> {
|
|
1019
|
-
const committedDb = await this
|
|
1117
|
+
const committedDb = await this.getWorldState(referenceBlock);
|
|
1020
1118
|
const [pathAndIndex] = await committedDb.findSiblingPaths<MerkleTreeId.NOTE_HASH_TREE>(
|
|
1021
1119
|
MerkleTreeId.NOTE_HASH_TREE,
|
|
1022
1120
|
[noteHash],
|
|
@@ -1030,7 +1128,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1030
1128
|
referenceBlock: BlockParameter,
|
|
1031
1129
|
l1ToL2Message: Fr,
|
|
1032
1130
|
): Promise<[bigint, SiblingPath<typeof L1_TO_L2_MSG_TREE_HEIGHT>] | undefined> {
|
|
1033
|
-
const db = await this
|
|
1131
|
+
const db = await this.getWorldState(referenceBlock);
|
|
1034
1132
|
const [witness] = await db.findSiblingPaths(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, [l1ToL2Message]);
|
|
1035
1133
|
if (!witness) {
|
|
1036
1134
|
return undefined;
|
|
@@ -1040,11 +1138,9 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1040
1138
|
return [witness.index, witness.path];
|
|
1041
1139
|
}
|
|
1042
1140
|
|
|
1043
|
-
public async
|
|
1141
|
+
public async getL1ToL2MessageCheckpoint(l1ToL2Message: Fr): Promise<CheckpointNumber | undefined> {
|
|
1044
1142
|
const messageIndex = await this.l1ToL2MessageSource.getL1ToL2MessageIndex(l1ToL2Message);
|
|
1045
|
-
return messageIndex
|
|
1046
|
-
? BlockNumber.fromCheckpointNumber(InboxLeaf.checkpointNumberFromIndex(messageIndex))
|
|
1047
|
-
: undefined;
|
|
1143
|
+
return messageIndex ? InboxLeaf.checkpointNumberFromIndex(messageIndex) : undefined;
|
|
1048
1144
|
}
|
|
1049
1145
|
|
|
1050
1146
|
/**
|
|
@@ -1065,19 +1161,9 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1065
1161
|
public async getL2ToL1Messages(epoch: EpochNumber): Promise<Fr[][][][]> {
|
|
1066
1162
|
// Assumes `getCheckpointedBlocksForEpoch` returns blocks in ascending order of block number.
|
|
1067
1163
|
const checkpointedBlocks = await this.blockSource.getCheckpointedBlocksForEpoch(epoch);
|
|
1068
|
-
const blocksInCheckpoints
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
for (const checkpointedBlock of checkpointedBlocks) {
|
|
1072
|
-
const block = checkpointedBlock.block;
|
|
1073
|
-
const slotNumber = block.header.globalVariables.slotNumber;
|
|
1074
|
-
if (slotNumber !== previousSlotNumber) {
|
|
1075
|
-
checkpointIndex++;
|
|
1076
|
-
blocksInCheckpoints.push([]);
|
|
1077
|
-
previousSlotNumber = slotNumber;
|
|
1078
|
-
}
|
|
1079
|
-
blocksInCheckpoints[checkpointIndex].push(block);
|
|
1080
|
-
}
|
|
1164
|
+
const blocksInCheckpoints = chunkBy(checkpointedBlocks, cb => cb.block.header.globalVariables.slotNumber).map(
|
|
1165
|
+
group => group.map(cb => cb.block),
|
|
1166
|
+
);
|
|
1081
1167
|
return blocksInCheckpoints.map(blocks =>
|
|
1082
1168
|
blocks.map(block => block.body.txEffects.map(txEffect => txEffect.l2ToL1Msgs)),
|
|
1083
1169
|
);
|
|
@@ -1087,7 +1173,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1087
1173
|
referenceBlock: BlockParameter,
|
|
1088
1174
|
nullifier: Fr,
|
|
1089
1175
|
): Promise<NullifierMembershipWitness | undefined> {
|
|
1090
|
-
const db = await this
|
|
1176
|
+
const db = await this.getWorldState(referenceBlock);
|
|
1091
1177
|
const [witness] = await db.findSiblingPaths(MerkleTreeId.NULLIFIER_TREE, [nullifier.toBuffer()]);
|
|
1092
1178
|
if (!witness) {
|
|
1093
1179
|
return undefined;
|
|
@@ -1102,33 +1188,20 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1102
1188
|
return new NullifierMembershipWitness(index, leafPreimage as NullifierLeafPreimage, path);
|
|
1103
1189
|
}
|
|
1104
1190
|
|
|
1105
|
-
/**
|
|
1106
|
-
* Returns a low nullifier membership witness for a given nullifier at a given block.
|
|
1107
|
-
* @param referenceBlock - The block parameter (block number, block hash, or 'latest') at which to get the data
|
|
1108
|
-
* (which contains the root of the nullifier tree in which we are searching for the nullifier).
|
|
1109
|
-
* @param nullifier - Nullifier we try to find the low nullifier witness for.
|
|
1110
|
-
* @returns The low nullifier membership witness (if found).
|
|
1111
|
-
* @remarks Low nullifier witness can be used to perform a nullifier non-inclusion proof by leveraging the "linked
|
|
1112
|
-
* list structure" of leaves and proving that a lower nullifier is pointing to a bigger next value than the nullifier
|
|
1113
|
-
* we are trying to prove non-inclusion for.
|
|
1114
|
-
*
|
|
1115
|
-
* Note: This function returns the membership witness of the nullifier itself and not the low nullifier when
|
|
1116
|
-
* the nullifier already exists in the tree. This is because the `getPreviousValueIndex` function returns the
|
|
1117
|
-
* index of the nullifier itself when it already exists in the tree.
|
|
1118
|
-
* TODO: This is a confusing behavior and we should eventually address that.
|
|
1119
|
-
*/
|
|
1120
1191
|
public async getLowNullifierMembershipWitness(
|
|
1121
1192
|
referenceBlock: BlockParameter,
|
|
1122
1193
|
nullifier: Fr,
|
|
1123
1194
|
): Promise<NullifierMembershipWitness | undefined> {
|
|
1124
|
-
const committedDb = await this
|
|
1195
|
+
const committedDb = await this.getWorldState(referenceBlock);
|
|
1125
1196
|
const findResult = await committedDb.getPreviousValueIndex(MerkleTreeId.NULLIFIER_TREE, nullifier.toBigInt());
|
|
1126
1197
|
if (!findResult) {
|
|
1127
1198
|
return undefined;
|
|
1128
1199
|
}
|
|
1129
1200
|
const { index, alreadyPresent } = findResult;
|
|
1130
1201
|
if (alreadyPresent) {
|
|
1131
|
-
|
|
1202
|
+
throw new Error(
|
|
1203
|
+
`Cannot prove nullifier non-inclusion: nullifier ${nullifier.toBigInt()} already exists in the tree`,
|
|
1204
|
+
);
|
|
1132
1205
|
}
|
|
1133
1206
|
const preimageData = (await committedDb.getLeafPreimage(MerkleTreeId.NULLIFIER_TREE, index))!;
|
|
1134
1207
|
|
|
@@ -1137,7 +1210,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1137
1210
|
}
|
|
1138
1211
|
|
|
1139
1212
|
async getPublicDataWitness(referenceBlock: BlockParameter, leafSlot: Fr): Promise<PublicDataWitness | undefined> {
|
|
1140
|
-
const committedDb = await this
|
|
1213
|
+
const committedDb = await this.getWorldState(referenceBlock);
|
|
1141
1214
|
const lowLeafResult = await committedDb.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot.toBigInt());
|
|
1142
1215
|
if (!lowLeafResult) {
|
|
1143
1216
|
return undefined;
|
|
@@ -1152,7 +1225,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1152
1225
|
}
|
|
1153
1226
|
|
|
1154
1227
|
public async getPublicStorageAt(referenceBlock: BlockParameter, contract: AztecAddress, slot: Fr): Promise<Fr> {
|
|
1155
|
-
const committedDb = await this
|
|
1228
|
+
const committedDb = await this.getWorldState(referenceBlock);
|
|
1156
1229
|
const leafSlot = await computePublicDataTreeLeafSlot(contract, slot);
|
|
1157
1230
|
|
|
1158
1231
|
const lowLeafResult = await committedDb.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot.toBigInt());
|
|
@@ -1266,7 +1339,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1266
1339
|
const processor = publicProcessorFactory.create(merkleTreeFork, newGlobalVariables, config);
|
|
1267
1340
|
|
|
1268
1341
|
// REFACTOR: Consider merging ProcessReturnValues into ProcessedTx
|
|
1269
|
-
const [processedTxs, failedTxs, _usedTxs, returns,
|
|
1342
|
+
const [processedTxs, failedTxs, _usedTxs, returns, debugLogs] = await processor.process([tx]);
|
|
1270
1343
|
// REFACTOR: Consider returning the error rather than throwing
|
|
1271
1344
|
if (failedTxs.length) {
|
|
1272
1345
|
this.log.warn(`Simulated tx ${txHash} fails: ${failedTxs[0].error}`, { txHash });
|
|
@@ -1292,11 +1365,12 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1292
1365
|
{ isSimulation, skipFeeEnforcement }: { isSimulation?: boolean; skipFeeEnforcement?: boolean } = {},
|
|
1293
1366
|
): Promise<TxValidationResult> {
|
|
1294
1367
|
const db = this.worldStateSynchronizer.getCommitted();
|
|
1295
|
-
const verifier = isSimulation ? undefined : this.
|
|
1368
|
+
const verifier = isSimulation ? undefined : this.rpcProofVerifier;
|
|
1296
1369
|
|
|
1297
1370
|
// We accept transactions if they are not expired by the next slot (checked based on the ExpirationTimestamp field)
|
|
1298
1371
|
const { ts: nextSlotTimestamp } = this.epochCache.getEpochAndSlotInNextL1Slot();
|
|
1299
1372
|
const blockNumber = BlockNumber((await this.blockSource.getBlockNumber()) + 1);
|
|
1373
|
+
const l1Constants = await this.blockSource.getL1Constants();
|
|
1300
1374
|
const validator = createTxValidatorForAcceptingTxsOverRPC(
|
|
1301
1375
|
db,
|
|
1302
1376
|
this.contractDataSource,
|
|
@@ -1306,10 +1380,16 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1306
1380
|
blockNumber,
|
|
1307
1381
|
l1ChainId: this.l1ChainId,
|
|
1308
1382
|
rollupVersion: this.version,
|
|
1309
|
-
setupAllowList:
|
|
1383
|
+
setupAllowList: [
|
|
1384
|
+
...(await getDefaultAllowedSetupFunctions()),
|
|
1385
|
+
...(this.config.txPublicSetupAllowListExtend ?? []),
|
|
1386
|
+
],
|
|
1310
1387
|
gasFees: await this.getCurrentMinFees(),
|
|
1311
1388
|
skipFeeEnforcement,
|
|
1312
1389
|
txsPermitted: !this.config.disableTransactions,
|
|
1390
|
+
rollupManaLimit: l1Constants.rollupManaLimit,
|
|
1391
|
+
maxBlockL2Gas: this.config.validateMaxL2BlockGas,
|
|
1392
|
+
maxBlockDAGas: this.config.validateMaxDABlockGas,
|
|
1313
1393
|
},
|
|
1314
1394
|
this.log.getBindings(),
|
|
1315
1395
|
);
|
|
@@ -1334,7 +1414,15 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1334
1414
|
archiver.updateConfig(config);
|
|
1335
1415
|
}
|
|
1336
1416
|
if (newConfig.realProofs !== this.config.realProofs) {
|
|
1337
|
-
|
|
1417
|
+
await Promise.all([tryStop(this.peerProofVerifier), tryStop(this.rpcProofVerifier)]);
|
|
1418
|
+
if (newConfig.realProofs) {
|
|
1419
|
+
this.peerProofVerifier = await BatchChonkVerifier.new(newConfig, newConfig.bbChonkVerifyMaxBatch, 'peer');
|
|
1420
|
+
const rpcVerifier = await BBCircuitVerifier.new(newConfig);
|
|
1421
|
+
this.rpcProofVerifier = new QueuedIVCVerifier(rpcVerifier, newConfig.numConcurrentIVCVerifiers);
|
|
1422
|
+
} else {
|
|
1423
|
+
this.peerProofVerifier = new TestCircuitVerifier();
|
|
1424
|
+
this.rpcProofVerifier = new TestCircuitVerifier();
|
|
1425
|
+
}
|
|
1338
1426
|
}
|
|
1339
1427
|
|
|
1340
1428
|
this.config = newConfig;
|
|
@@ -1409,7 +1497,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1409
1497
|
return Promise.resolve();
|
|
1410
1498
|
}
|
|
1411
1499
|
|
|
1412
|
-
public async rollbackTo(targetBlock: BlockNumber, force?: boolean): Promise<void> {
|
|
1500
|
+
public async rollbackTo(targetBlock: BlockNumber, force?: boolean, resumeSync = true): Promise<void> {
|
|
1413
1501
|
const archiver = this.blockSource as Archiver;
|
|
1414
1502
|
if (!('rollbackTo' in archiver)) {
|
|
1415
1503
|
throw new Error('Archiver implementation does not support rollbacks.');
|
|
@@ -1439,9 +1527,13 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1439
1527
|
this.log.error(`Error during rollback`, err);
|
|
1440
1528
|
throw err;
|
|
1441
1529
|
} finally {
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1530
|
+
if (resumeSync) {
|
|
1531
|
+
this.log.info(`Resuming world state and archiver sync.`);
|
|
1532
|
+
this.worldStateSynchronizer.resumeSync();
|
|
1533
|
+
archiver.resume();
|
|
1534
|
+
} else {
|
|
1535
|
+
this.log.info(`Sync left paused after rollback (resumeSync=false).`);
|
|
1536
|
+
}
|
|
1445
1537
|
}
|
|
1446
1538
|
}
|
|
1447
1539
|
|
|
@@ -1458,19 +1550,12 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1458
1550
|
return Promise.resolve();
|
|
1459
1551
|
}
|
|
1460
1552
|
|
|
1461
|
-
public getSlashPayloads(): Promise<SlashPayloadRound[]> {
|
|
1462
|
-
if (!this.slasherClient) {
|
|
1463
|
-
throw new Error(`Slasher client not enabled`);
|
|
1464
|
-
}
|
|
1465
|
-
return this.slasherClient.getSlashPayloads();
|
|
1466
|
-
}
|
|
1467
|
-
|
|
1468
1553
|
public getSlashOffenses(round: bigint | 'all' | 'current'): Promise<Offense[]> {
|
|
1469
1554
|
if (!this.slasherClient) {
|
|
1470
1555
|
throw new Error(`Slasher client not enabled`);
|
|
1471
1556
|
}
|
|
1472
1557
|
if (round === 'all') {
|
|
1473
|
-
return this.slasherClient.
|
|
1558
|
+
return this.slasherClient.getOffenses();
|
|
1474
1559
|
} else {
|
|
1475
1560
|
return this.slasherClient.gatherOffensesForRound(round === 'current' ? undefined : BigInt(round));
|
|
1476
1561
|
}
|
|
@@ -1564,6 +1649,40 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1564
1649
|
this.log.info('Keystore reloaded: coinbase, feeRecipient, and attester keys updated');
|
|
1565
1650
|
}
|
|
1566
1651
|
|
|
1652
|
+
public async mineBlock(): Promise<void> {
|
|
1653
|
+
if (!this.sequencer) {
|
|
1654
|
+
throw new BadRequestError('Cannot mine block: no sequencer is running');
|
|
1655
|
+
}
|
|
1656
|
+
|
|
1657
|
+
const currentBlockNumber = await this.getBlockNumber();
|
|
1658
|
+
|
|
1659
|
+
// Use slot duration + 50% buffer as the timeout so this works on running networks too
|
|
1660
|
+
const { slotDuration } = await this.blockSource.getL1Constants();
|
|
1661
|
+
const timeoutSeconds = Math.ceil(slotDuration * 1.5);
|
|
1662
|
+
|
|
1663
|
+
// Temporarily set minTxsPerBlock to 0 so the sequencer produces a block even with no txs
|
|
1664
|
+
const originalMinTxsPerBlock = this.sequencer.getSequencer().getConfig().minTxsPerBlock;
|
|
1665
|
+
this.sequencer.updateConfig({ minTxsPerBlock: 0 });
|
|
1666
|
+
|
|
1667
|
+
try {
|
|
1668
|
+
// Trigger the sequencer to produce a block immediately
|
|
1669
|
+
void this.sequencer.trigger();
|
|
1670
|
+
|
|
1671
|
+
// Wait for the new L2 block to appear
|
|
1672
|
+
await retryUntil(
|
|
1673
|
+
async () => {
|
|
1674
|
+
const newBlockNumber = await this.getBlockNumber();
|
|
1675
|
+
return newBlockNumber > currentBlockNumber ? true : undefined;
|
|
1676
|
+
},
|
|
1677
|
+
'mineBlock',
|
|
1678
|
+
timeoutSeconds,
|
|
1679
|
+
0.1,
|
|
1680
|
+
);
|
|
1681
|
+
} finally {
|
|
1682
|
+
this.sequencer.updateConfig({ minTxsPerBlock: originalMinTxsPerBlock });
|
|
1683
|
+
}
|
|
1684
|
+
}
|
|
1685
|
+
|
|
1567
1686
|
#getInitialHeaderHash(): Promise<BlockHash> {
|
|
1568
1687
|
if (!this.initialHeaderHashPromise) {
|
|
1569
1688
|
this.initialHeaderHashPromise = this.worldStateSynchronizer.getCommitted().getInitialHeader().hash();
|
|
@@ -1576,7 +1695,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1576
1695
|
* @param block - The block parameter (block number, block hash, or 'latest') at which to get the data.
|
|
1577
1696
|
* @returns An instance of a committed MerkleTreeOperations
|
|
1578
1697
|
*/
|
|
1579
|
-
async
|
|
1698
|
+
protected async getWorldState(block: BlockParameter) {
|
|
1580
1699
|
let blockSyncedTo: BlockNumber = BlockNumber.ZERO;
|
|
1581
1700
|
try {
|
|
1582
1701
|
// Attempt to sync the world state if necessary
|
|
@@ -1590,6 +1709,8 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1590
1709
|
return this.worldStateSynchronizer.getCommitted();
|
|
1591
1710
|
}
|
|
1592
1711
|
|
|
1712
|
+
// Get the block number, either directly from the parameter or by quering the archiver with the block hash
|
|
1713
|
+
let blockNumber: BlockNumber;
|
|
1593
1714
|
if (BlockHash.isBlockHash(block)) {
|
|
1594
1715
|
const initialBlockHash = await this.#getInitialHeaderHash();
|
|
1595
1716
|
if (block.equals(initialBlockHash)) {
|
|
@@ -1603,22 +1724,50 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1603
1724
|
`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.`,
|
|
1604
1725
|
);
|
|
1605
1726
|
}
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1727
|
+
|
|
1728
|
+
blockNumber = header.getBlockNumber();
|
|
1729
|
+
} else {
|
|
1730
|
+
blockNumber = block as BlockNumber;
|
|
1609
1731
|
}
|
|
1610
1732
|
|
|
1611
|
-
//
|
|
1612
|
-
{
|
|
1613
|
-
|
|
1733
|
+
// Check it's within world state sync range
|
|
1734
|
+
if (blockNumber > blockSyncedTo) {
|
|
1735
|
+
throw new Error(`Queried block ${block} not yet synced by the node (node is synced upto ${blockSyncedTo}).`);
|
|
1736
|
+
}
|
|
1737
|
+
this.log.debug(`Using snapshot for block ${blockNumber}, world state synced upto ${blockSyncedTo}`);
|
|
1614
1738
|
|
|
1615
|
-
|
|
1616
|
-
|
|
1739
|
+
const snapshot = this.worldStateSynchronizer.getSnapshot(blockNumber);
|
|
1740
|
+
|
|
1741
|
+
// Double-check world-state synced to the same block hash as was requested
|
|
1742
|
+
if (BlockHash.isBlockHash(block)) {
|
|
1743
|
+
const blockHash = await snapshot.getLeafValue(MerkleTreeId.ARCHIVE, BigInt(blockNumber));
|
|
1744
|
+
if (!blockHash || !block.equals(blockHash)) {
|
|
1745
|
+
throw new Error(
|
|
1746
|
+
`Block hash ${block.toString()} not found in world state at block number ${blockNumber}. If the node API has been queried with anchor block hash possibly a reorg has occurred.`,
|
|
1747
|
+
);
|
|
1617
1748
|
}
|
|
1749
|
+
}
|
|
1750
|
+
|
|
1751
|
+
return snapshot;
|
|
1752
|
+
}
|
|
1618
1753
|
|
|
1619
|
-
|
|
1620
|
-
|
|
1754
|
+
/** Resolves a block parameter to a block number. */
|
|
1755
|
+
protected async resolveBlockNumber(block: BlockParameter): Promise<BlockNumber> {
|
|
1756
|
+
if (block === 'latest') {
|
|
1757
|
+
return BlockNumber(await this.blockSource.getBlockNumber());
|
|
1758
|
+
}
|
|
1759
|
+
if (BlockHash.isBlockHash(block)) {
|
|
1760
|
+
const initialBlockHash = await this.#getInitialHeaderHash();
|
|
1761
|
+
if (block.equals(initialBlockHash)) {
|
|
1762
|
+
return BlockNumber.ZERO;
|
|
1763
|
+
}
|
|
1764
|
+
const header = await this.blockSource.getBlockHeaderByHash(block);
|
|
1765
|
+
if (!header) {
|
|
1766
|
+
throw new Error(`Block hash ${block.toString()} not found.`);
|
|
1767
|
+
}
|
|
1768
|
+
return header.getBlockNumber();
|
|
1621
1769
|
}
|
|
1770
|
+
return block as BlockNumber;
|
|
1622
1771
|
}
|
|
1623
1772
|
|
|
1624
1773
|
/**
|