@aztec/aztec-node 0.0.1-commit.5de5ca79e → 0.0.1-commit.6201a7b05
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dest/aztec-node/block_response_helpers.d.ts +21 -0
- package/dest/aztec-node/block_response_helpers.d.ts.map +1 -0
- package/dest/aztec-node/block_response_helpers.js +88 -0
- package/dest/aztec-node/server.d.ts +36 -66
- package/dest/aztec-node/server.d.ts.map +1 -1
- package/dest/aztec-node/server.js +634 -332
- package/dest/sentinel/factory.d.ts +3 -2
- package/dest/sentinel/factory.d.ts.map +1 -1
- package/dest/sentinel/sentinel.d.ts +6 -3
- package/dest/sentinel/sentinel.d.ts.map +1 -1
- package/dest/sentinel/sentinel.js +9 -3
- package/package.json +26 -26
- package/src/aztec-node/block_response_helpers.ts +131 -0
- package/src/aztec-node/server.ts +747 -442
- package/src/sentinel/factory.ts +2 -1
- package/src/sentinel/sentinel.ts +19 -7
package/src/aztec-node/server.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
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';
|
|
@@ -8,12 +9,14 @@ import { createEthereumChain } from '@aztec/ethereum/chain';
|
|
|
8
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
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,
|
|
@@ -42,14 +50,13 @@ import {
|
|
|
42
50
|
import { CollectionLimitsConfig, PublicSimulatorConfig } from '@aztec/stdlib/avm';
|
|
43
51
|
import { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
44
52
|
import {
|
|
45
|
-
type BlockData,
|
|
46
53
|
BlockHash,
|
|
47
54
|
type BlockParameter,
|
|
48
55
|
type DataInBlock,
|
|
49
56
|
L2Block,
|
|
50
57
|
type L2BlockSource,
|
|
58
|
+
inspectBlockParameter,
|
|
51
59
|
} from '@aztec/stdlib/block';
|
|
52
|
-
import type { PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
|
|
53
60
|
import type {
|
|
54
61
|
ContractClassPublic,
|
|
55
62
|
ContractDataSource,
|
|
@@ -57,16 +64,24 @@ import type {
|
|
|
57
64
|
NodeInfo,
|
|
58
65
|
ProtocolContractAddresses,
|
|
59
66
|
} from '@aztec/stdlib/contract';
|
|
60
|
-
import { GasFees } from '@aztec/stdlib/gas';
|
|
67
|
+
import { GasFees, type ManaUsageEstimate } from '@aztec/stdlib/gas';
|
|
61
68
|
import { computePublicDataTreeLeafSlot } from '@aztec/stdlib/hash';
|
|
62
|
-
import {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
+
import type {
|
|
70
|
+
AztecNode,
|
|
71
|
+
AztecNodeAdmin,
|
|
72
|
+
AztecNodeAdminConfig,
|
|
73
|
+
AztecNodeDebug,
|
|
74
|
+
BlockIncludeOptions,
|
|
75
|
+
BlockResponse,
|
|
76
|
+
ChainTip,
|
|
77
|
+
ChainTips,
|
|
78
|
+
CheckpointIncludeOptions,
|
|
79
|
+
CheckpointParameter,
|
|
80
|
+
CheckpointResponse,
|
|
81
|
+
GetContractClassLogsResponse,
|
|
82
|
+
GetPublicLogsResponse,
|
|
69
83
|
} from '@aztec/stdlib/interfaces/client';
|
|
84
|
+
import { AztecNodeAdminConfigSchema } from '@aztec/stdlib/interfaces/client';
|
|
70
85
|
import {
|
|
71
86
|
type AllowedElement,
|
|
72
87
|
type ClientProtocolCircuitVerifier,
|
|
@@ -79,11 +94,12 @@ import {
|
|
|
79
94
|
import type { DebugLogStore, LogFilter, SiloedTag, Tag, TxScopedL2Log } from '@aztec/stdlib/logs';
|
|
80
95
|
import { InMemoryDebugLogStore, NullDebugLogStore } from '@aztec/stdlib/logs';
|
|
81
96
|
import { InboxLeaf, type L1ToL2MessageSource } from '@aztec/stdlib/messaging';
|
|
82
|
-
import type { Offense
|
|
83
|
-
import type { NullifierLeafPreimage,
|
|
97
|
+
import type { Offense } from '@aztec/stdlib/slashing';
|
|
98
|
+
import type { NullifierLeafPreimage, PublicDataTreeLeafPreimage } from '@aztec/stdlib/trees';
|
|
84
99
|
import { MerkleTreeId, NullifierMembershipWitness, PublicDataWitness } from '@aztec/stdlib/trees';
|
|
85
100
|
import {
|
|
86
101
|
type BlockHeader,
|
|
102
|
+
type FeeProvider,
|
|
87
103
|
type GlobalVariableBuilder as GlobalVariableBuilderInterface,
|
|
88
104
|
type IndexedTxEffect,
|
|
89
105
|
PublicSimulationOutput,
|
|
@@ -95,6 +111,7 @@ import {
|
|
|
95
111
|
} from '@aztec/stdlib/tx';
|
|
96
112
|
import { getPackageVersion } from '@aztec/stdlib/update-checker';
|
|
97
113
|
import type { SingleValidatorStats, ValidatorsStats } from '@aztec/stdlib/validators';
|
|
114
|
+
import type { GenesisData } from '@aztec/stdlib/world-state';
|
|
98
115
|
import {
|
|
99
116
|
Attributes,
|
|
100
117
|
type TelemetryClient,
|
|
@@ -108,7 +125,7 @@ import {
|
|
|
108
125
|
FullNodeCheckpointsBuilder,
|
|
109
126
|
NodeKeystoreAdapter,
|
|
110
127
|
ValidatorClient,
|
|
111
|
-
|
|
128
|
+
createProposalHandler,
|
|
112
129
|
createValidatorClient,
|
|
113
130
|
} from '@aztec/validator-client';
|
|
114
131
|
import type { SlashingProtectionDatabase } from '@aztec/validator-ha-signer/types';
|
|
@@ -118,13 +135,19 @@ import { createPublicClient } from 'viem';
|
|
|
118
135
|
|
|
119
136
|
import { createSentinel } from '../sentinel/factory.js';
|
|
120
137
|
import { Sentinel } from '../sentinel/sentinel.js';
|
|
138
|
+
import {
|
|
139
|
+
blockResponseFromBlockData,
|
|
140
|
+
blockResponseFromL2Block,
|
|
141
|
+
checkpointResponseFromCheckpointData,
|
|
142
|
+
checkpointResponseFromPublishedCheckpoint,
|
|
143
|
+
} from './block_response_helpers.js';
|
|
121
144
|
import { type AztecNodeConfig, createKeyStoreForValidator } from './config.js';
|
|
122
145
|
import { NodeMetrics } from './node_metrics.js';
|
|
123
146
|
|
|
124
147
|
/**
|
|
125
148
|
* The aztec node.
|
|
126
149
|
*/
|
|
127
|
-
export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
150
|
+
export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDebug, Traceable {
|
|
128
151
|
private metrics: NodeMetrics;
|
|
129
152
|
private initialHeaderHashPromise: Promise<BlockHash> | undefined = undefined;
|
|
130
153
|
|
|
@@ -146,12 +169,15 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
146
169
|
protected readonly slasherClient: SlasherClientInterface | undefined,
|
|
147
170
|
protected readonly validatorsSentinel: Sentinel | undefined,
|
|
148
171
|
protected readonly epochPruneWatcher: EpochPruneWatcher | undefined,
|
|
172
|
+
protected readonly attestationsBlockWatcher: AttestationsBlockWatcher | undefined,
|
|
149
173
|
protected readonly l1ChainId: number,
|
|
150
174
|
protected readonly version: number,
|
|
151
175
|
protected readonly globalVariableBuilder: GlobalVariableBuilderInterface,
|
|
176
|
+
protected readonly feeProvider: FeeProvider,
|
|
152
177
|
protected readonly epochCache: EpochCacheInterface,
|
|
153
178
|
protected readonly packageVersion: string,
|
|
154
|
-
private
|
|
179
|
+
private peerProofVerifier: ClientProtocolCircuitVerifier,
|
|
180
|
+
private rpcProofVerifier: ClientProtocolCircuitVerifier,
|
|
155
181
|
private telemetry: TelemetryClient = getTelemetryClient(),
|
|
156
182
|
private log = createLogger('node'),
|
|
157
183
|
private blobClient?: BlobClientInterface,
|
|
@@ -173,15 +199,291 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
173
199
|
}
|
|
174
200
|
}
|
|
175
201
|
|
|
202
|
+
/** @internal Exposed for testing — returns the RPC proof verifier. */
|
|
203
|
+
public getProofVerifier(): ClientProtocolCircuitVerifier {
|
|
204
|
+
return this.rpcProofVerifier;
|
|
205
|
+
}
|
|
206
|
+
|
|
176
207
|
public async getWorldStateSyncStatus(): Promise<WorldStateSyncStatus> {
|
|
177
208
|
const status = await this.worldStateSynchronizer.status();
|
|
178
209
|
return status.syncSummary;
|
|
179
210
|
}
|
|
180
211
|
|
|
212
|
+
public async getChainTips(): Promise<ChainTips> {
|
|
213
|
+
const { proposed, checkpointed, proven, finalized } = await this.blockSource.getL2Tips();
|
|
214
|
+
return { proposed, checkpointed, proven, finalized };
|
|
215
|
+
}
|
|
216
|
+
|
|
181
217
|
public getL2Tips() {
|
|
182
218
|
return this.blockSource.getL2Tips();
|
|
183
219
|
}
|
|
184
220
|
|
|
221
|
+
public async getBlockHeader(number: BlockNumber | 'latest'): Promise<BlockHeader | undefined> {
|
|
222
|
+
const resolvedNumber = number === 'latest' ? await this.blockSource.getBlockNumber() : number;
|
|
223
|
+
if (resolvedNumber === BlockNumber.ZERO) {
|
|
224
|
+
return this.worldStateSynchronizer.getCommitted().getInitialHeader();
|
|
225
|
+
}
|
|
226
|
+
return this.blockSource.getBlockHeader(resolvedNumber);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
public async getCheckpointedBlocks(from: BlockNumber, limit: number) {
|
|
230
|
+
return (await this.blockSource.getCheckpointedBlocks(from, limit)) ?? [];
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
public getCheckpointsDataForEpoch(epoch: EpochNumber) {
|
|
234
|
+
return this.blockSource.getCheckpointsDataForEpoch(epoch);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
public getBlockNumber(tip?: ChainTip): Promise<BlockNumber> {
|
|
238
|
+
switch (tip) {
|
|
239
|
+
case undefined:
|
|
240
|
+
case 'proposed':
|
|
241
|
+
return this.blockSource.getBlockNumber();
|
|
242
|
+
case 'checkpointed':
|
|
243
|
+
return this.blockSource.getCheckpointedL2BlockNumber();
|
|
244
|
+
case 'proven':
|
|
245
|
+
return this.blockSource.getProvenBlockNumber();
|
|
246
|
+
case 'finalized':
|
|
247
|
+
return this.blockSource.getFinalizedL2BlockNumber();
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
public async getCheckpointNumber(tip?: ChainTip): Promise<CheckpointNumber> {
|
|
252
|
+
switch (tip) {
|
|
253
|
+
case undefined:
|
|
254
|
+
case 'proposed':
|
|
255
|
+
case 'checkpointed':
|
|
256
|
+
return await this.blockSource.getCheckpointNumber();
|
|
257
|
+
case 'proven':
|
|
258
|
+
case 'finalized': {
|
|
259
|
+
const tips = await this.blockSource.getL2Tips();
|
|
260
|
+
return tip === 'proven' ? tips.proven.checkpoint.number : tips.finalized.checkpoint.number;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
private isChainTip(value: unknown): value is ChainTip {
|
|
266
|
+
return value === 'proposed' || value === 'checkpointed' || value === 'proven' || value === 'finalized';
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
private async resolveBlockParameter(
|
|
270
|
+
param: BlockParameter,
|
|
271
|
+
): Promise<{ number?: BlockNumber; hash?: BlockHash; archive?: Fr }> {
|
|
272
|
+
if (BlockHash.isBlockHash(param)) {
|
|
273
|
+
return { hash: param };
|
|
274
|
+
}
|
|
275
|
+
if (typeof param === 'number') {
|
|
276
|
+
return { number: param as BlockNumber };
|
|
277
|
+
}
|
|
278
|
+
if (param === 'latest') {
|
|
279
|
+
return { number: await this.blockSource.getBlockNumber() };
|
|
280
|
+
}
|
|
281
|
+
if (this.isChainTip(param)) {
|
|
282
|
+
return { number: await this.getBlockNumber(param) };
|
|
283
|
+
}
|
|
284
|
+
if (typeof param === 'object' && param !== null) {
|
|
285
|
+
if ('number' in param) {
|
|
286
|
+
return { number: param.number };
|
|
287
|
+
}
|
|
288
|
+
if ('hash' in param) {
|
|
289
|
+
return { hash: param.hash };
|
|
290
|
+
}
|
|
291
|
+
if ('archive' in param) {
|
|
292
|
+
return { archive: param.archive };
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
throw new BadRequestError(`Invalid BlockParameter: ${JSON.stringify(param)}`);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
private async resolveCheckpointParameter(
|
|
299
|
+
param: CheckpointParameter,
|
|
300
|
+
): Promise<{ number?: CheckpointNumber; slot?: SlotNumber }> {
|
|
301
|
+
if (typeof param === 'number') {
|
|
302
|
+
return { number: param as CheckpointNumber };
|
|
303
|
+
}
|
|
304
|
+
if (param === 'latest') {
|
|
305
|
+
return { number: await this.blockSource.getCheckpointNumber() };
|
|
306
|
+
}
|
|
307
|
+
if (this.isChainTip(param)) {
|
|
308
|
+
return { number: await this.getCheckpointNumber(param) };
|
|
309
|
+
}
|
|
310
|
+
if (typeof param === 'object' && param !== null) {
|
|
311
|
+
if ('number' in param) {
|
|
312
|
+
return { number: param.number };
|
|
313
|
+
}
|
|
314
|
+
if ('slot' in param) {
|
|
315
|
+
return { slot: param.slot };
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
throw new BadRequestError(`Invalid CheckpointParameter: ${JSON.stringify(param)}`);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
public async getBlock<Opts extends BlockIncludeOptions = {}>(
|
|
322
|
+
param: BlockParameter,
|
|
323
|
+
options: Opts = {} as Opts,
|
|
324
|
+
): Promise<BlockResponse<Opts> | undefined> {
|
|
325
|
+
const resolved = await this.resolveBlockParameter(param);
|
|
326
|
+
const wantTxs = !!options.includeTransactions;
|
|
327
|
+
const wantContext = !!options.includeL1PublishInfo || !!options.includeAttestations;
|
|
328
|
+
|
|
329
|
+
if (resolved.hash !== undefined) {
|
|
330
|
+
const initial = await this.#getInitialHeaderHash();
|
|
331
|
+
if (resolved.hash.equals(initial)) {
|
|
332
|
+
return (await this.buildGenesisBlockResponse(options)) as BlockResponse<Opts>;
|
|
333
|
+
}
|
|
334
|
+
if (wantTxs) {
|
|
335
|
+
const block = await this.blockSource.getL2BlockByHash(resolved.hash);
|
|
336
|
+
if (!block) {
|
|
337
|
+
return undefined;
|
|
338
|
+
}
|
|
339
|
+
const ctx = wantContext ? await this.blockSource.getBlockDataWithCheckpointContext(block.number) : undefined;
|
|
340
|
+
return (await blockResponseFromL2Block(block, options, ctx)) as BlockResponse<Opts>;
|
|
341
|
+
}
|
|
342
|
+
const data = await this.blockSource.getBlockHeaderByHash(resolved.hash);
|
|
343
|
+
if (!data) {
|
|
344
|
+
return undefined;
|
|
345
|
+
}
|
|
346
|
+
const blockNumber = data.globalVariables.blockNumber;
|
|
347
|
+
const ctx = wantContext ? await this.blockSource.getBlockDataWithCheckpointContext(blockNumber) : undefined;
|
|
348
|
+
if (ctx) {
|
|
349
|
+
return blockResponseFromBlockData(ctx.data, blockNumber, options, ctx) as BlockResponse<Opts>;
|
|
350
|
+
}
|
|
351
|
+
const blockData = await this.blockSource.getBlockData(blockNumber);
|
|
352
|
+
if (!blockData) {
|
|
353
|
+
return undefined;
|
|
354
|
+
}
|
|
355
|
+
return blockResponseFromBlockData(blockData, blockNumber, options) as BlockResponse<Opts>;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
if (resolved.archive !== undefined) {
|
|
359
|
+
if (wantTxs) {
|
|
360
|
+
const block = await this.blockSource.getL2BlockByArchive(resolved.archive);
|
|
361
|
+
if (!block) {
|
|
362
|
+
return undefined;
|
|
363
|
+
}
|
|
364
|
+
const ctx = wantContext ? await this.blockSource.getBlockDataWithCheckpointContext(block.number) : undefined;
|
|
365
|
+
return (await blockResponseFromL2Block(block, options, ctx)) as BlockResponse<Opts>;
|
|
366
|
+
}
|
|
367
|
+
const data = await this.blockSource.getBlockDataByArchive(resolved.archive);
|
|
368
|
+
if (!data) {
|
|
369
|
+
return undefined;
|
|
370
|
+
}
|
|
371
|
+
const blockNumber = data.header.globalVariables.blockNumber;
|
|
372
|
+
const ctx = wantContext ? await this.blockSource.getBlockDataWithCheckpointContext(blockNumber) : undefined;
|
|
373
|
+
return blockResponseFromBlockData(data, blockNumber, options, ctx) as BlockResponse<Opts>;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
const blockNumber = resolved.number!;
|
|
377
|
+
if (blockNumber === BlockNumber.ZERO) {
|
|
378
|
+
return (await this.buildGenesisBlockResponse(options)) as BlockResponse<Opts>;
|
|
379
|
+
}
|
|
380
|
+
if (wantTxs) {
|
|
381
|
+
const block = await this.blockSource.getL2Block(blockNumber);
|
|
382
|
+
if (!block) {
|
|
383
|
+
return undefined;
|
|
384
|
+
}
|
|
385
|
+
const ctx = wantContext ? await this.blockSource.getBlockDataWithCheckpointContext(blockNumber) : undefined;
|
|
386
|
+
return (await blockResponseFromL2Block(block, options, ctx)) as BlockResponse<Opts>;
|
|
387
|
+
}
|
|
388
|
+
const ctx = await this.blockSource.getBlockDataWithCheckpointContext(blockNumber);
|
|
389
|
+
if (!ctx) {
|
|
390
|
+
return undefined;
|
|
391
|
+
}
|
|
392
|
+
return blockResponseFromBlockData(ctx.data, blockNumber, options, ctx) as BlockResponse<Opts>;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
public async getBlocks<Opts extends BlockIncludeOptions = {}>(
|
|
396
|
+
from: BlockNumber,
|
|
397
|
+
limit: number,
|
|
398
|
+
options: Opts = {} as Opts,
|
|
399
|
+
): Promise<BlockResponse<Opts>[]> {
|
|
400
|
+
const wantTxs = !!options.includeTransactions;
|
|
401
|
+
const wantContext = !!options.includeL1PublishInfo || !!options.includeAttestations;
|
|
402
|
+
if (wantTxs) {
|
|
403
|
+
const blocks = await this.blockSource.getBlocks(from, limit);
|
|
404
|
+
return (await Promise.all(
|
|
405
|
+
blocks.map(async block => {
|
|
406
|
+
const ctx = wantContext ? await this.blockSource.getBlockDataWithCheckpointContext(block.number) : undefined;
|
|
407
|
+
return blockResponseFromL2Block(block, options, ctx);
|
|
408
|
+
}),
|
|
409
|
+
)) as BlockResponse<Opts>[];
|
|
410
|
+
}
|
|
411
|
+
const results: BlockResponse<Opts>[] = [];
|
|
412
|
+
for (let i = 0; i < limit; i++) {
|
|
413
|
+
const blockNumber = BlockNumber(from + i);
|
|
414
|
+
const ctx = await this.blockSource.getBlockDataWithCheckpointContext(blockNumber);
|
|
415
|
+
if (!ctx) {
|
|
416
|
+
break;
|
|
417
|
+
}
|
|
418
|
+
results.push(blockResponseFromBlockData(ctx.data, blockNumber, options, ctx) as BlockResponse<Opts>);
|
|
419
|
+
}
|
|
420
|
+
return results;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
public async getCheckpoint<Opts extends CheckpointIncludeOptions = {}>(
|
|
424
|
+
param: CheckpointParameter,
|
|
425
|
+
options: Opts = {} as Opts,
|
|
426
|
+
): Promise<CheckpointResponse<Opts> | undefined> {
|
|
427
|
+
const resolved = await this.resolveCheckpointParameter(param);
|
|
428
|
+
let checkpointNumber = resolved.number;
|
|
429
|
+
if (checkpointNumber === undefined && resolved.slot !== undefined) {
|
|
430
|
+
checkpointNumber = await this.blockSource.getCheckpointNumberBySlot(resolved.slot);
|
|
431
|
+
}
|
|
432
|
+
if (checkpointNumber === undefined) {
|
|
433
|
+
return undefined;
|
|
434
|
+
}
|
|
435
|
+
if (options.includeBlocks) {
|
|
436
|
+
const [checkpoint] = await this.blockSource.getCheckpoints(checkpointNumber, 1);
|
|
437
|
+
if (!checkpoint) {
|
|
438
|
+
return undefined;
|
|
439
|
+
}
|
|
440
|
+
return (await checkpointResponseFromPublishedCheckpoint(checkpoint, options)) as CheckpointResponse<Opts>;
|
|
441
|
+
}
|
|
442
|
+
const data = await this.blockSource.getCheckpointData(checkpointNumber);
|
|
443
|
+
if (!data) {
|
|
444
|
+
return undefined;
|
|
445
|
+
}
|
|
446
|
+
return checkpointResponseFromCheckpointData(data, options) as CheckpointResponse<Opts>;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
public async getCheckpoints<Opts extends CheckpointIncludeOptions = {}>(
|
|
450
|
+
from: CheckpointNumber,
|
|
451
|
+
limit: number,
|
|
452
|
+
options: Opts = {} as Opts,
|
|
453
|
+
): Promise<CheckpointResponse<Opts>[]> {
|
|
454
|
+
if (options.includeBlocks) {
|
|
455
|
+
const checkpoints = await this.blockSource.getCheckpoints(from, limit);
|
|
456
|
+
return (await Promise.all(
|
|
457
|
+
checkpoints.map(cp => checkpointResponseFromPublishedCheckpoint(cp, options)),
|
|
458
|
+
)) as CheckpointResponse<Opts>[];
|
|
459
|
+
}
|
|
460
|
+
const datas = await this.blockSource.getCheckpointDataRange(from, limit);
|
|
461
|
+
return datas.map(d => checkpointResponseFromCheckpointData(d, options)) as CheckpointResponse<Opts>[];
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
private async buildGenesisBlockResponse(options: BlockIncludeOptions): Promise<BlockResponse> {
|
|
465
|
+
const initial = this.worldStateSynchronizer.getCommitted().getInitialHeader();
|
|
466
|
+
const empty = L2Block.empty(initial);
|
|
467
|
+
const response: BlockResponse = {
|
|
468
|
+
header: empty.header,
|
|
469
|
+
archive: empty.archive,
|
|
470
|
+
hash: await this.#getInitialHeaderHash(),
|
|
471
|
+
checkpointNumber: empty.checkpointNumber,
|
|
472
|
+
indexWithinCheckpoint: empty.indexWithinCheckpoint,
|
|
473
|
+
number: empty.number,
|
|
474
|
+
};
|
|
475
|
+
if (options.includeTransactions) {
|
|
476
|
+
(response as BlockResponse).body = empty.body;
|
|
477
|
+
}
|
|
478
|
+
if (options.includeL1PublishInfo) {
|
|
479
|
+
(response as BlockResponse).l1 = { published: false };
|
|
480
|
+
}
|
|
481
|
+
if (options.includeAttestations) {
|
|
482
|
+
(response as BlockResponse).attestations = [];
|
|
483
|
+
}
|
|
484
|
+
return response;
|
|
485
|
+
}
|
|
486
|
+
|
|
185
487
|
/**
|
|
186
488
|
* initializes the Aztec Node, wait for component to sync.
|
|
187
489
|
* @param config - The configuration to be used by the aztec node.
|
|
@@ -199,7 +501,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
199
501
|
slashingProtectionDb?: SlashingProtectionDatabase;
|
|
200
502
|
} = {},
|
|
201
503
|
options: {
|
|
202
|
-
|
|
504
|
+
genesis?: GenesisData;
|
|
203
505
|
dontStartSequencer?: boolean;
|
|
204
506
|
dontStartProverNode?: boolean;
|
|
205
507
|
} = {},
|
|
@@ -295,299 +597,354 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
295
597
|
|
|
296
598
|
const epochCache = await EpochCache.create(config.l1Contracts.rollupAddress, config, { dateProvider });
|
|
297
599
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
const worldStateSynchronizer = await createWorldStateSynchronizer(
|
|
306
|
-
config,
|
|
307
|
-
archiver,
|
|
308
|
-
options.prefilledPublicData,
|
|
309
|
-
telemetry,
|
|
310
|
-
);
|
|
311
|
-
const circuitVerifier =
|
|
312
|
-
config.realProofs || config.debugForceTxProofVerification
|
|
313
|
-
? await BBCircuitVerifier.new(config)
|
|
314
|
-
: new TestCircuitVerifier(config.proverTestVerificationDelayMs);
|
|
315
|
-
|
|
316
|
-
let debugLogStore: DebugLogStore;
|
|
317
|
-
if (!config.realProofs) {
|
|
318
|
-
log.warn(`Aztec node is accepting fake proofs`);
|
|
319
|
-
|
|
320
|
-
debugLogStore = new InMemoryDebugLogStore();
|
|
321
|
-
log.info(
|
|
322
|
-
'Aztec node started in test mode (realProofs set to false) hence debug logs from public functions will be collected and served',
|
|
600
|
+
// Track started resources so we can clean up on partial failure during node creation.
|
|
601
|
+
const started: { stop?(): Promise<void> | void }[] = [];
|
|
602
|
+
try {
|
|
603
|
+
const archiver = await createArchiver(
|
|
604
|
+
config,
|
|
605
|
+
{ blobClient, epochCache, telemetry, dateProvider },
|
|
606
|
+
{ blockUntilSync: !config.skipArchiverInitialSync },
|
|
323
607
|
);
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
608
|
+
started.push(archiver);
|
|
609
|
+
|
|
610
|
+
// now create the merkle trees and the world state synchronizer
|
|
611
|
+
const worldStateSynchronizer = await createWorldStateSynchronizer(config, archiver, options.genesis, telemetry);
|
|
612
|
+
started.push(worldStateSynchronizer);
|
|
613
|
+
const useRealVerifiers = config.realProofs || config.debugForceTxProofVerification;
|
|
614
|
+
let peerProofVerifier: ClientProtocolCircuitVerifier;
|
|
615
|
+
let rpcProofVerifier: ClientProtocolCircuitVerifier;
|
|
616
|
+
if (useRealVerifiers) {
|
|
617
|
+
peerProofVerifier = await BatchChonkVerifier.new(config, config.bbChonkVerifyMaxBatch, 'peer');
|
|
618
|
+
const rpcVerifier = await BBCircuitVerifier.new(config);
|
|
619
|
+
rpcProofVerifier = new QueuedIVCVerifier(rpcVerifier, config.numConcurrentIVCVerifiers);
|
|
620
|
+
} else {
|
|
621
|
+
peerProofVerifier = new TestCircuitVerifier(config.proverTestVerificationDelayMs);
|
|
622
|
+
rpcProofVerifier = new TestCircuitVerifier(config.proverTestVerificationDelayMs);
|
|
623
|
+
}
|
|
624
|
+
started.push(peerProofVerifier, rpcProofVerifier);
|
|
334
625
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
archiver,
|
|
339
|
-
proofVerifier,
|
|
340
|
-
worldStateSynchronizer,
|
|
341
|
-
epochCache,
|
|
342
|
-
packageVersion,
|
|
343
|
-
dateProvider,
|
|
344
|
-
telemetry,
|
|
345
|
-
deps.p2pClientDeps,
|
|
346
|
-
);
|
|
626
|
+
let debugLogStore: DebugLogStore;
|
|
627
|
+
if (!config.realProofs) {
|
|
628
|
+
log.warn(`Aztec node is accepting fake proofs`);
|
|
347
629
|
|
|
348
|
-
|
|
349
|
-
|
|
630
|
+
debugLogStore = new InMemoryDebugLogStore();
|
|
631
|
+
log.info(
|
|
632
|
+
'Aztec node started in test mode (realProofs set to false) hence debug logs from public functions will be collected and served',
|
|
633
|
+
);
|
|
634
|
+
} else {
|
|
635
|
+
debugLogStore = new NullDebugLogStore();
|
|
636
|
+
}
|
|
350
637
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
...config,
|
|
638
|
+
const globalVariableBuilderConfig = {
|
|
639
|
+
l1Contracts: config.l1Contracts,
|
|
640
|
+
ethereumSlotDuration: config.ethereumSlotDuration,
|
|
641
|
+
rollupVersion: BigInt(config.rollupVersion),
|
|
356
642
|
l1GenesisTime,
|
|
357
643
|
slotDuration: Number(slotDuration),
|
|
358
|
-
|
|
359
|
-
maxTxsPerCheckpoint: config.validateMaxTxsPerCheckpoint,
|
|
360
|
-
},
|
|
361
|
-
worldStateSynchronizer,
|
|
362
|
-
archiver,
|
|
363
|
-
dateProvider,
|
|
364
|
-
telemetry,
|
|
365
|
-
);
|
|
366
|
-
|
|
367
|
-
let validatorClient: ValidatorClient | undefined;
|
|
644
|
+
};
|
|
368
645
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
validatorClient = await createValidatorClient(config, {
|
|
372
|
-
checkpointsBuilder: validatorCheckpointsBuilder,
|
|
373
|
-
worldState: worldStateSynchronizer,
|
|
374
|
-
p2pClient,
|
|
375
|
-
telemetry,
|
|
376
|
-
dateProvider,
|
|
377
|
-
epochCache,
|
|
378
|
-
blockSource: archiver,
|
|
379
|
-
l1ToL2MessageSource: archiver,
|
|
380
|
-
keyStoreManager,
|
|
381
|
-
blobClient,
|
|
382
|
-
slashingProtectionDb: deps.slashingProtectionDb,
|
|
383
|
-
});
|
|
646
|
+
const globalVariableBuilder = new GlobalVariableBuilder(dateProvider, publicClient, globalVariableBuilderConfig);
|
|
647
|
+
const feeProvider = new FeeProviderImpl(dateProvider, publicClient, globalVariableBuilderConfig);
|
|
384
648
|
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
if (validatorClient) {
|
|
389
|
-
watchers.push(validatorClient);
|
|
390
|
-
if (!options.dontStartSequencer) {
|
|
391
|
-
await validatorClient.registerHandlers();
|
|
392
|
-
}
|
|
649
|
+
const proverOnly = config.enableProverNode && config.disableValidator;
|
|
650
|
+
if (proverOnly) {
|
|
651
|
+
log.info('Starting in prover-only mode: skipping validator, sequencer, sentinel, and slasher subsystems');
|
|
393
652
|
}
|
|
394
|
-
}
|
|
395
653
|
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
createBlockProposalHandler(config, {
|
|
403
|
-
checkpointsBuilder: validatorCheckpointsBuilder,
|
|
404
|
-
worldState: worldStateSynchronizer,
|
|
654
|
+
// create the tx pool and the p2p client, which will need the l2 block source
|
|
655
|
+
const p2pClient = await createP2PClient(
|
|
656
|
+
config,
|
|
657
|
+
archiver,
|
|
658
|
+
peerProofVerifier,
|
|
659
|
+
worldStateSynchronizer,
|
|
405
660
|
epochCache,
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
p2pClient,
|
|
661
|
+
feeProvider,
|
|
662
|
+
packageVersion,
|
|
409
663
|
dateProvider,
|
|
410
664
|
telemetry,
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
665
|
+
deps.p2pClientDeps,
|
|
666
|
+
);
|
|
667
|
+
started.push(p2pClient);
|
|
668
|
+
|
|
669
|
+
// We'll accumulate sentinel watchers here
|
|
670
|
+
const watchers: Watcher[] = [];
|
|
671
|
+
|
|
672
|
+
// Create FullNodeCheckpointsBuilder for block proposal handling and tx validation.
|
|
673
|
+
// Override maxTxsPerCheckpoint with the validator-specific limit if set.
|
|
674
|
+
const validatorCheckpointsBuilder = new FullNodeCheckpointsBuilder(
|
|
675
|
+
{
|
|
676
|
+
...config,
|
|
677
|
+
l1GenesisTime,
|
|
678
|
+
slotDuration: Number(slotDuration),
|
|
679
|
+
rollupManaLimit,
|
|
680
|
+
maxTxsPerCheckpoint: config.validateMaxTxsPerCheckpoint,
|
|
681
|
+
},
|
|
682
|
+
worldStateSynchronizer,
|
|
683
|
+
archiver,
|
|
684
|
+
dateProvider,
|
|
685
|
+
telemetry,
|
|
686
|
+
);
|
|
419
687
|
|
|
420
|
-
|
|
421
|
-
let epochPruneWatcher: EpochPruneWatcher | undefined;
|
|
422
|
-
let attestationsBlockWatcher: AttestationsBlockWatcher | undefined;
|
|
688
|
+
let validatorClient: ValidatorClient | undefined;
|
|
423
689
|
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
690
|
+
if (!config.disableValidator) {
|
|
691
|
+
// Create validator client if required
|
|
692
|
+
validatorClient = await createValidatorClient(config, {
|
|
693
|
+
checkpointsBuilder: validatorCheckpointsBuilder,
|
|
694
|
+
worldState: worldStateSynchronizer,
|
|
695
|
+
p2pClient,
|
|
696
|
+
telemetry,
|
|
697
|
+
dateProvider,
|
|
698
|
+
epochCache,
|
|
699
|
+
blockSource: archiver,
|
|
700
|
+
l1ToL2MessageSource: archiver,
|
|
701
|
+
keyStoreManager,
|
|
702
|
+
blobClient,
|
|
703
|
+
slashingProtectionDb: deps.slashingProtectionDb,
|
|
704
|
+
});
|
|
705
|
+
|
|
706
|
+
// If we have a validator client, register it as a source of offenses for the slasher,
|
|
707
|
+
// and have it register callbacks on the p2p client *before* we start it, otherwise messages
|
|
708
|
+
// like attestations or auths will fail.
|
|
709
|
+
if (validatorClient) {
|
|
710
|
+
watchers.push(validatorClient);
|
|
711
|
+
|
|
712
|
+
const vc = validatorClient;
|
|
713
|
+
const getValidatorAddresses = () => vc.getValidatorAddresses().map(a => a.toString());
|
|
714
|
+
validatorClient.getProposalHandler().register(p2pClient, true, archiver, getValidatorAddresses);
|
|
715
|
+
|
|
716
|
+
if (!options.dontStartSequencer) {
|
|
717
|
+
await validatorClient.registerHandlers();
|
|
718
|
+
}
|
|
719
|
+
}
|
|
428
720
|
}
|
|
429
721
|
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
722
|
+
// If there's no validator client, create a ProposalHandler to handle block and checkpoint proposals
|
|
723
|
+
// for monitoring or reexecution. Reexecution (default) allows us to follow the pending chain,
|
|
724
|
+
// while non-reexecution is used for validating the proposals and collecting their txs.
|
|
725
|
+
// Checkpoint proposals rebuild blobs if the blob client can upload blobs.
|
|
726
|
+
if (!validatorClient) {
|
|
727
|
+
const reexecute = !!config.alwaysReexecuteBlockProposals;
|
|
728
|
+
log.info(`Setting up proposal handler` + (reexecute ? ' with reexecution of proposals' : ''));
|
|
729
|
+
createProposalHandler(config, {
|
|
730
|
+
checkpointsBuilder: validatorCheckpointsBuilder,
|
|
731
|
+
worldState: worldStateSynchronizer,
|
|
434
732
|
epochCache,
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
733
|
+
blockSource: archiver,
|
|
734
|
+
l1ToL2MessageSource: archiver,
|
|
735
|
+
p2pClient,
|
|
736
|
+
blobClient,
|
|
737
|
+
dateProvider,
|
|
738
|
+
telemetry,
|
|
739
|
+
}).register(p2pClient, reexecute, archiver);
|
|
440
740
|
}
|
|
441
741
|
|
|
442
|
-
//
|
|
443
|
-
|
|
444
|
-
attestationsBlockWatcher = new AttestationsBlockWatcher(archiver, epochCache, config);
|
|
445
|
-
watchers.push(attestationsBlockWatcher);
|
|
446
|
-
}
|
|
447
|
-
}
|
|
742
|
+
// Start world state and wait for it to sync to the archiver.
|
|
743
|
+
await worldStateSynchronizer.start();
|
|
448
744
|
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
.waitForInitialSync()
|
|
452
|
-
.then(async () => {
|
|
453
|
-
await p2pClient.start();
|
|
454
|
-
await validatorsSentinel?.start();
|
|
455
|
-
await epochPruneWatcher?.start();
|
|
456
|
-
await attestationsBlockWatcher?.start();
|
|
457
|
-
log.info(`All p2p services started`);
|
|
458
|
-
})
|
|
459
|
-
.catch(err => log.error('Failed to start p2p services after archiver sync', err));
|
|
460
|
-
|
|
461
|
-
// Validator enabled, create/start relevant service
|
|
462
|
-
let sequencer: SequencerClient | undefined;
|
|
463
|
-
let slasherClient: SlasherClientInterface | undefined;
|
|
464
|
-
if (!config.disableValidator && validatorClient) {
|
|
465
|
-
// We create a slasher only if we have a sequencer, since all slashing actions go through the sequencer publisher
|
|
466
|
-
// as they are executed when the node is selected as proposer.
|
|
467
|
-
const validatorAddresses = keyStoreManager
|
|
468
|
-
? NodeKeystoreAdapter.fromKeyStoreManager(keyStoreManager).getAddresses()
|
|
469
|
-
: [];
|
|
470
|
-
|
|
471
|
-
slasherClient = await createSlasher(
|
|
472
|
-
config,
|
|
473
|
-
config.l1Contracts,
|
|
474
|
-
getPublicClient(config),
|
|
475
|
-
watchers,
|
|
476
|
-
dateProvider,
|
|
477
|
-
epochCache,
|
|
478
|
-
validatorAddresses,
|
|
479
|
-
undefined, // logger
|
|
480
|
-
);
|
|
481
|
-
await slasherClient.start();
|
|
745
|
+
// Start p2p. Note that it depends on world state to be running.
|
|
746
|
+
await p2pClient.start();
|
|
482
747
|
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
)
|
|
491
|
-
|
|
748
|
+
let validatorsSentinel: Awaited<ReturnType<typeof createSentinel>> | undefined;
|
|
749
|
+
let epochPruneWatcher: EpochPruneWatcher | undefined;
|
|
750
|
+
let attestationsBlockWatcher: AttestationsBlockWatcher | undefined;
|
|
751
|
+
|
|
752
|
+
if (!proverOnly) {
|
|
753
|
+
validatorsSentinel = await createSentinel(epochCache, archiver, p2pClient, config);
|
|
754
|
+
if (validatorsSentinel && config.slashInactivityPenalty > 0n) {
|
|
755
|
+
watchers.push(validatorsSentinel);
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
if (config.slashPrunePenalty > 0n || config.slashDataWithholdingPenalty > 0n) {
|
|
759
|
+
epochPruneWatcher = new EpochPruneWatcher(
|
|
760
|
+
archiver,
|
|
761
|
+
archiver,
|
|
762
|
+
epochCache,
|
|
763
|
+
p2pClient.getTxProvider(),
|
|
764
|
+
validatorCheckpointsBuilder,
|
|
765
|
+
config,
|
|
766
|
+
);
|
|
767
|
+
watchers.push(epochPruneWatcher);
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
// We assume we want to slash for invalid attestations unless all max penalties are set to 0
|
|
771
|
+
if (config.slashProposeInvalidAttestationsPenalty > 0n || config.slashAttestDescendantOfInvalidPenalty > 0n) {
|
|
772
|
+
attestationsBlockWatcher = new AttestationsBlockWatcher(archiver, epochCache, config);
|
|
773
|
+
watchers.push(attestationsBlockWatcher);
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
// Start p2p-related services once the archiver has completed sync
|
|
778
|
+
void archiver
|
|
779
|
+
.waitForInitialSync()
|
|
780
|
+
.then(async () => {
|
|
781
|
+
if (validatorsSentinel) {
|
|
782
|
+
await validatorsSentinel.start();
|
|
783
|
+
started.push(validatorsSentinel);
|
|
784
|
+
}
|
|
785
|
+
if (epochPruneWatcher) {
|
|
786
|
+
await epochPruneWatcher.start();
|
|
787
|
+
started.push(epochPruneWatcher);
|
|
788
|
+
}
|
|
789
|
+
if (attestationsBlockWatcher) {
|
|
790
|
+
await attestationsBlockWatcher.start();
|
|
791
|
+
started.push(attestationsBlockWatcher);
|
|
792
|
+
}
|
|
793
|
+
log.info(`All p2p services started`);
|
|
794
|
+
})
|
|
795
|
+
.catch(err => log.error('Failed to start p2p services after archiver sync', err));
|
|
796
|
+
|
|
797
|
+
// Validator enabled, create/start relevant service
|
|
798
|
+
let sequencer: SequencerClient | undefined;
|
|
799
|
+
let slasherClient: SlasherClientInterface | undefined;
|
|
800
|
+
if (!config.disableValidator && validatorClient) {
|
|
801
|
+
// We create a slasher only if we have a sequencer, since all slashing actions go through the sequencer publisher
|
|
802
|
+
// as they are executed when the node is selected as proposer.
|
|
803
|
+
const validatorAddresses = keyStoreManager
|
|
804
|
+
? NodeKeystoreAdapter.fromKeyStoreManager(keyStoreManager).getAddresses()
|
|
805
|
+
: [];
|
|
806
|
+
|
|
807
|
+
slasherClient = await createSlasher(
|
|
808
|
+
config,
|
|
809
|
+
config.l1Contracts,
|
|
810
|
+
getPublicClient(config),
|
|
811
|
+
watchers,
|
|
812
|
+
dateProvider,
|
|
813
|
+
epochCache,
|
|
814
|
+
validatorAddresses,
|
|
815
|
+
undefined, // logger
|
|
816
|
+
);
|
|
817
|
+
await slasherClient.start();
|
|
818
|
+
started.push(slasherClient);
|
|
819
|
+
|
|
820
|
+
const l1TxUtils = config.sequencerPublisherForwarderAddress
|
|
821
|
+
? await createForwarderL1TxUtilsFromSigners(
|
|
822
|
+
publicClient,
|
|
823
|
+
keyStoreManager!.createAllValidatorPublisherSigners(),
|
|
824
|
+
config.sequencerPublisherForwarderAddress,
|
|
825
|
+
{ ...config, scope: 'sequencer' },
|
|
826
|
+
{ telemetry, logger: log.createChild('l1-tx-utils'), dateProvider, kzg: Blob.getViemKzgInstance() },
|
|
827
|
+
)
|
|
828
|
+
: await createL1TxUtilsFromSigners(
|
|
829
|
+
publicClient,
|
|
830
|
+
keyStoreManager!.createAllValidatorPublisherSigners(),
|
|
831
|
+
{ ...config, scope: 'sequencer' },
|
|
832
|
+
{ telemetry, logger: log.createChild('l1-tx-utils'), dateProvider, kzg: Blob.getViemKzgInstance() },
|
|
833
|
+
);
|
|
834
|
+
|
|
835
|
+
// Create a funder L1TxUtils from the keystore funding account (if configured)
|
|
836
|
+
const fundingSigner = keyStoreManager?.createFundingSigner();
|
|
837
|
+
let funderL1TxUtils: L1TxUtils | undefined;
|
|
838
|
+
if (fundingSigner) {
|
|
839
|
+
const [funder] = await createL1TxUtilsFromSigners(
|
|
492
840
|
publicClient,
|
|
493
|
-
|
|
841
|
+
[fundingSigner],
|
|
494
842
|
{ ...config, scope: 'sequencer' },
|
|
495
|
-
{ telemetry, logger: log.createChild('l1-tx-utils'), dateProvider
|
|
843
|
+
{ telemetry, logger: log.createChild('l1-tx-utils:funder'), dateProvider },
|
|
496
844
|
);
|
|
845
|
+
funderL1TxUtils = funder;
|
|
846
|
+
}
|
|
497
847
|
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
848
|
+
// Create and start the sequencer client
|
|
849
|
+
const checkpointsBuilder = new CheckpointsBuilder(
|
|
850
|
+
{ ...config, l1GenesisTime, slotDuration: Number(slotDuration), rollupManaLimit },
|
|
851
|
+
worldStateSynchronizer,
|
|
852
|
+
archiver,
|
|
853
|
+
dateProvider,
|
|
854
|
+
telemetry,
|
|
855
|
+
debugLogStore,
|
|
856
|
+
);
|
|
507
857
|
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
858
|
+
sequencer = await SequencerClient.new(config, {
|
|
859
|
+
...deps,
|
|
860
|
+
epochCache,
|
|
861
|
+
l1TxUtils,
|
|
862
|
+
funderL1TxUtils,
|
|
863
|
+
validatorClient,
|
|
864
|
+
p2pClient,
|
|
865
|
+
worldStateSynchronizer,
|
|
866
|
+
slasherClient,
|
|
867
|
+
checkpointsBuilder,
|
|
868
|
+
l2BlockSource: archiver,
|
|
869
|
+
l1ToL2MessageSource: archiver,
|
|
870
|
+
telemetry,
|
|
871
|
+
dateProvider,
|
|
872
|
+
blobClient,
|
|
873
|
+
nodeKeyStore: keyStoreManager!,
|
|
874
|
+
globalVariableBuilder,
|
|
875
|
+
});
|
|
876
|
+
}
|
|
525
877
|
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
878
|
+
if (!options.dontStartSequencer && sequencer) {
|
|
879
|
+
await sequencer.start();
|
|
880
|
+
started.push(sequencer);
|
|
881
|
+
log.verbose(`Sequencer started`);
|
|
882
|
+
} else if (sequencer) {
|
|
883
|
+
log.warn(`Sequencer created but not started`);
|
|
884
|
+
}
|
|
532
885
|
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
886
|
+
// Create prover node subsystem if enabled
|
|
887
|
+
let proverNode: ProverNode | undefined;
|
|
888
|
+
if (config.enableProverNode) {
|
|
889
|
+
proverNode = await createProverNode(config, {
|
|
890
|
+
...deps.proverNodeDeps,
|
|
891
|
+
telemetry,
|
|
892
|
+
dateProvider,
|
|
893
|
+
archiver,
|
|
894
|
+
worldStateSynchronizer,
|
|
895
|
+
p2pClient,
|
|
896
|
+
epochCache,
|
|
897
|
+
blobClient,
|
|
898
|
+
keyStoreManager,
|
|
899
|
+
});
|
|
900
|
+
|
|
901
|
+
if (!options.dontStartProverNode) {
|
|
902
|
+
await proverNode.start();
|
|
903
|
+
started.push(proverNode);
|
|
904
|
+
log.info(`Prover node subsystem started`);
|
|
905
|
+
} else {
|
|
906
|
+
log.info(`Prover node subsystem created but not started`);
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
const node = new AztecNodeService(
|
|
911
|
+
config,
|
|
912
|
+
p2pClient,
|
|
913
|
+
archiver,
|
|
914
|
+
archiver,
|
|
915
|
+
archiver,
|
|
540
916
|
archiver,
|
|
541
917
|
worldStateSynchronizer,
|
|
542
|
-
|
|
918
|
+
sequencer,
|
|
919
|
+
proverNode,
|
|
920
|
+
slasherClient,
|
|
921
|
+
validatorsSentinel,
|
|
922
|
+
epochPruneWatcher,
|
|
923
|
+
attestationsBlockWatcher,
|
|
924
|
+
ethereumChain.chainInfo.id,
|
|
925
|
+
config.rollupVersion,
|
|
926
|
+
globalVariableBuilder,
|
|
927
|
+
feeProvider,
|
|
543
928
|
epochCache,
|
|
929
|
+
packageVersion,
|
|
930
|
+
peerProofVerifier,
|
|
931
|
+
rpcProofVerifier,
|
|
932
|
+
telemetry,
|
|
933
|
+
log,
|
|
544
934
|
blobClient,
|
|
935
|
+
validatorClient,
|
|
545
936
|
keyStoreManager,
|
|
546
|
-
|
|
937
|
+
debugLogStore,
|
|
938
|
+
);
|
|
547
939
|
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
940
|
+
return node;
|
|
941
|
+
} catch (err) {
|
|
942
|
+
log.error('Failed during node creation, stopping started resources', err);
|
|
943
|
+
for (const resource of started.reverse()) {
|
|
944
|
+
await tryStop(resource);
|
|
553
945
|
}
|
|
946
|
+
throw err;
|
|
554
947
|
}
|
|
555
|
-
|
|
556
|
-
const globalVariableBuilder = new GlobalVariableBuilder({
|
|
557
|
-
...config,
|
|
558
|
-
rollupVersion: BigInt(config.rollupVersion),
|
|
559
|
-
l1GenesisTime,
|
|
560
|
-
slotDuration: Number(slotDuration),
|
|
561
|
-
});
|
|
562
|
-
|
|
563
|
-
const node = new AztecNodeService(
|
|
564
|
-
config,
|
|
565
|
-
p2pClient,
|
|
566
|
-
archiver,
|
|
567
|
-
archiver,
|
|
568
|
-
archiver,
|
|
569
|
-
archiver,
|
|
570
|
-
worldStateSynchronizer,
|
|
571
|
-
sequencer,
|
|
572
|
-
proverNode,
|
|
573
|
-
slasherClient,
|
|
574
|
-
validatorsSentinel,
|
|
575
|
-
epochPruneWatcher,
|
|
576
|
-
ethereumChain.chainInfo.id,
|
|
577
|
-
config.rollupVersion,
|
|
578
|
-
globalVariableBuilder,
|
|
579
|
-
epochCache,
|
|
580
|
-
packageVersion,
|
|
581
|
-
proofVerifier,
|
|
582
|
-
telemetry,
|
|
583
|
-
log,
|
|
584
|
-
blobClient,
|
|
585
|
-
validatorClient,
|
|
586
|
-
keyStoreManager,
|
|
587
|
-
debugLogStore,
|
|
588
|
-
);
|
|
589
|
-
|
|
590
|
-
return node;
|
|
591
948
|
}
|
|
592
949
|
|
|
593
950
|
/**
|
|
@@ -662,77 +1019,13 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
662
1019
|
return nodeInfo;
|
|
663
1020
|
}
|
|
664
1021
|
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
* @param block - The block parameter (block number, block hash, or 'latest').
|
|
668
|
-
* @returns The requested block.
|
|
669
|
-
*/
|
|
670
|
-
public async getBlock(block: BlockParameter): Promise<L2Block | undefined> {
|
|
671
|
-
if (BlockHash.isBlockHash(block)) {
|
|
672
|
-
return this.getBlockByHash(block);
|
|
673
|
-
}
|
|
674
|
-
const blockNumber = block === 'latest' ? await this.getBlockNumber() : (block as BlockNumber);
|
|
675
|
-
if (blockNumber === BlockNumber.ZERO) {
|
|
676
|
-
return this.buildInitialBlock();
|
|
677
|
-
}
|
|
678
|
-
return await this.blockSource.getL2Block(blockNumber);
|
|
679
|
-
}
|
|
680
|
-
|
|
681
|
-
/**
|
|
682
|
-
* Get a block specified by its hash.
|
|
683
|
-
* @param blockHash - The block hash being requested.
|
|
684
|
-
* @returns The requested block.
|
|
685
|
-
*/
|
|
686
|
-
public async getBlockByHash(blockHash: BlockHash): Promise<L2Block | undefined> {
|
|
687
|
-
const initialBlockHash = await this.#getInitialHeaderHash();
|
|
688
|
-
if (blockHash.equals(initialBlockHash)) {
|
|
689
|
-
return this.buildInitialBlock();
|
|
690
|
-
}
|
|
691
|
-
return await this.blockSource.getL2BlockByHash(blockHash);
|
|
692
|
-
}
|
|
693
|
-
|
|
694
|
-
private buildInitialBlock(): L2Block {
|
|
695
|
-
const initialHeader = this.worldStateSynchronizer.getCommitted().getInitialHeader();
|
|
696
|
-
return L2Block.empty(initialHeader);
|
|
697
|
-
}
|
|
698
|
-
|
|
699
|
-
/**
|
|
700
|
-
* Get a block specified by its archive root.
|
|
701
|
-
* @param archive - The archive root being requested.
|
|
702
|
-
* @returns The requested block.
|
|
703
|
-
*/
|
|
704
|
-
public async getBlockByArchive(archive: Fr): Promise<L2Block | undefined> {
|
|
705
|
-
return await this.blockSource.getL2BlockByArchive(archive);
|
|
706
|
-
}
|
|
707
|
-
|
|
708
|
-
/**
|
|
709
|
-
* Method to request blocks. Will attempt to return all requested blocks but will return only those available.
|
|
710
|
-
* @param from - The start of the range of blocks to return.
|
|
711
|
-
* @param limit - The maximum number of blocks to obtain.
|
|
712
|
-
* @returns The blocks requested.
|
|
713
|
-
*/
|
|
714
|
-
public async getBlocks(from: BlockNumber, limit: number): Promise<L2Block[]> {
|
|
715
|
-
return (await this.blockSource.getBlocks(from, BlockNumber(limit))) ?? [];
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
public async getCheckpoints(from: CheckpointNumber, limit: number): Promise<PublishedCheckpoint[]> {
|
|
719
|
-
return (await this.blockSource.getCheckpoints(from, limit)) ?? [];
|
|
720
|
-
}
|
|
721
|
-
|
|
722
|
-
public async getCheckpointedBlocks(from: BlockNumber, limit: number) {
|
|
723
|
-
return (await this.blockSource.getCheckpointedBlocks(from, limit)) ?? [];
|
|
1022
|
+
public async getCurrentMinFees(): Promise<GasFees> {
|
|
1023
|
+
return await this.feeProvider.getCurrentMinFees();
|
|
724
1024
|
}
|
|
725
1025
|
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
/**
|
|
731
|
-
* Method to fetch the current min L2 fees.
|
|
732
|
-
* @returns The current min L2 fees.
|
|
733
|
-
*/
|
|
734
|
-
public async getCurrentMinFees(): Promise<GasFees> {
|
|
735
|
-
return await this.globalVariableBuilder.getCurrentMinFees();
|
|
1026
|
+
/** Returns predicted min fees for the current slot and next N slots. */
|
|
1027
|
+
public async getPredictedMinFees(manaUsage?: ManaUsageEstimate): Promise<GasFees[]> {
|
|
1028
|
+
return await this.feeProvider.getPredictedMinFees(manaUsage);
|
|
736
1029
|
}
|
|
737
1030
|
|
|
738
1031
|
public async getMaxPriorityFees(): Promise<GasFees> {
|
|
@@ -743,26 +1036,6 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
743
1036
|
return GasFees.from({ feePerDaGas: 0n, feePerL2Gas: 0n });
|
|
744
1037
|
}
|
|
745
1038
|
|
|
746
|
-
/**
|
|
747
|
-
* Method to fetch the latest block number synchronized by the node.
|
|
748
|
-
* @returns The block number.
|
|
749
|
-
*/
|
|
750
|
-
public async getBlockNumber(): Promise<BlockNumber> {
|
|
751
|
-
return await this.blockSource.getBlockNumber();
|
|
752
|
-
}
|
|
753
|
-
|
|
754
|
-
public async getProvenBlockNumber(): Promise<BlockNumber> {
|
|
755
|
-
return await this.blockSource.getProvenBlockNumber();
|
|
756
|
-
}
|
|
757
|
-
|
|
758
|
-
public async getCheckpointedBlockNumber(): Promise<BlockNumber> {
|
|
759
|
-
return await this.blockSource.getCheckpointedL2BlockNumber();
|
|
760
|
-
}
|
|
761
|
-
|
|
762
|
-
public getCheckpointNumber(): Promise<CheckpointNumber> {
|
|
763
|
-
return this.blockSource.getCheckpointNumber();
|
|
764
|
-
}
|
|
765
|
-
|
|
766
1039
|
/**
|
|
767
1040
|
* Method to fetch the version of the package.
|
|
768
1041
|
* @returns The node package version
|
|
@@ -880,7 +1153,13 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
880
1153
|
throw new Error(`Invalid tx: ${reason}`);
|
|
881
1154
|
}
|
|
882
1155
|
|
|
883
|
-
|
|
1156
|
+
try {
|
|
1157
|
+
await this.p2pClient!.sendTx(tx);
|
|
1158
|
+
} catch (err) {
|
|
1159
|
+
this.metrics.receivedTx(timer.ms(), false);
|
|
1160
|
+
this.log.warn(`Mempool rejected tx ${txHash}: ${(err as Error).message}`, { txHash });
|
|
1161
|
+
throw err;
|
|
1162
|
+
}
|
|
884
1163
|
const duration = timer.ms();
|
|
885
1164
|
this.metrics.receivedTx(duration, true);
|
|
886
1165
|
this.log.info(`Received tx ${txHash} in ${duration}ms`, { txHash });
|
|
@@ -902,10 +1181,15 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
902
1181
|
// If the tx is in the pool but not in the archiver, it's pending.
|
|
903
1182
|
// This handles race conditions between archiver and p2p, where the archiver
|
|
904
1183
|
// has pruned the block in which a tx was mined, but p2p has not caught up yet.
|
|
905
|
-
receipt = new TxReceipt(txHash, TxStatus.PENDING, undefined, undefined);
|
|
1184
|
+
receipt = new TxReceipt(txHash, TxStatus.PENDING, /*executionResult=*/ undefined, /*error=*/ undefined);
|
|
906
1185
|
} else {
|
|
907
1186
|
// Otherwise, if we don't know the tx, we consider it dropped.
|
|
908
|
-
receipt = new TxReceipt(
|
|
1187
|
+
receipt = new TxReceipt(
|
|
1188
|
+
txHash,
|
|
1189
|
+
TxStatus.DROPPED,
|
|
1190
|
+
/*executionResult=*/ undefined,
|
|
1191
|
+
/*error=*/ 'Tx dropped by P2P node',
|
|
1192
|
+
);
|
|
909
1193
|
}
|
|
910
1194
|
|
|
911
1195
|
this.debugLogStore.decorateReceiptWithLogs(txHash.toString(), receipt);
|
|
@@ -922,10 +1206,11 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
922
1206
|
*/
|
|
923
1207
|
public async stop() {
|
|
924
1208
|
this.log.info(`Stopping Aztec Node`);
|
|
1209
|
+
await tryStop(this.attestationsBlockWatcher);
|
|
925
1210
|
await tryStop(this.validatorsSentinel);
|
|
926
1211
|
await tryStop(this.epochPruneWatcher);
|
|
927
1212
|
await tryStop(this.slasherClient);
|
|
928
|
-
await tryStop(this.
|
|
1213
|
+
await Promise.all([tryStop(this.peerProofVerifier), tryStop(this.rpcProofVerifier)]);
|
|
929
1214
|
await tryStop(this.sequencer);
|
|
930
1215
|
await tryStop(this.proverNode);
|
|
931
1216
|
await tryStop(this.p2pClient);
|
|
@@ -1015,7 +1300,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1015
1300
|
);
|
|
1016
1301
|
|
|
1017
1302
|
// Build a map from block number to block hash
|
|
1018
|
-
const blockNumberToHash = new Map<BlockNumber,
|
|
1303
|
+
const blockNumberToHash = new Map<BlockNumber, BlockHash>();
|
|
1019
1304
|
for (let i = 0; i < uniqueBlockNumbers.length; i++) {
|
|
1020
1305
|
const blockHash = blockHashes[i];
|
|
1021
1306
|
if (blockHash === undefined) {
|
|
@@ -1033,13 +1318,13 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1033
1318
|
if (blockNumber === undefined) {
|
|
1034
1319
|
throw new Error(`Block number not found for leaf index ${index} in tree ${MerkleTreeId[treeId]}`);
|
|
1035
1320
|
}
|
|
1036
|
-
const
|
|
1037
|
-
if (
|
|
1321
|
+
const l2BlockHash = blockNumberToHash.get(blockNumber);
|
|
1322
|
+
if (l2BlockHash === undefined) {
|
|
1038
1323
|
throw new Error(`Block hash not found for block number ${blockNumber}`);
|
|
1039
1324
|
}
|
|
1040
1325
|
return {
|
|
1041
1326
|
l2BlockNumber: blockNumber,
|
|
1042
|
-
l2BlockHash
|
|
1327
|
+
l2BlockHash,
|
|
1043
1328
|
data: index,
|
|
1044
1329
|
};
|
|
1045
1330
|
});
|
|
@@ -1053,6 +1338,10 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1053
1338
|
// which is the archive tree root BEFORE the anchor block was added (i.e. the state after block N-1).
|
|
1054
1339
|
// So we need the world state at block N-1, not block N, to produce a sibling path matching that root.
|
|
1055
1340
|
const referenceBlockNumber = await this.resolveBlockNumber(referenceBlock);
|
|
1341
|
+
if (referenceBlockNumber === BlockNumber.ZERO) {
|
|
1342
|
+
// Block 0 (the initial block) has an empty archive, so no membership witness can exist.
|
|
1343
|
+
return undefined;
|
|
1344
|
+
}
|
|
1056
1345
|
const committedDb = await this.getWorldState(BlockNumber(referenceBlockNumber - 1));
|
|
1057
1346
|
const [pathAndIndex] = await committedDb.findSiblingPaths<MerkleTreeId.ARCHIVE>(MerkleTreeId.ARCHIVE, [blockHash]);
|
|
1058
1347
|
return pathAndIndex === undefined
|
|
@@ -1189,41 +1478,6 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1189
1478
|
return preimage.leaf.value;
|
|
1190
1479
|
}
|
|
1191
1480
|
|
|
1192
|
-
public async getBlockHeader(block: BlockParameter = 'latest'): Promise<BlockHeader | undefined> {
|
|
1193
|
-
if (BlockHash.isBlockHash(block)) {
|
|
1194
|
-
const initialBlockHash = await this.#getInitialHeaderHash();
|
|
1195
|
-
if (block.equals(initialBlockHash)) {
|
|
1196
|
-
// Block source doesn't handle initial header so we need to handle the case separately.
|
|
1197
|
-
return this.worldStateSynchronizer.getCommitted().getInitialHeader();
|
|
1198
|
-
}
|
|
1199
|
-
return this.blockSource.getBlockHeaderByHash(block);
|
|
1200
|
-
} else {
|
|
1201
|
-
// Block source doesn't handle initial header so we need to handle the case separately.
|
|
1202
|
-
const blockNumber = block === 'latest' ? await this.getBlockNumber() : (block as BlockNumber);
|
|
1203
|
-
if (blockNumber === BlockNumber.ZERO) {
|
|
1204
|
-
return this.worldStateSynchronizer.getCommitted().getInitialHeader();
|
|
1205
|
-
}
|
|
1206
|
-
return this.blockSource.getBlockHeader(block);
|
|
1207
|
-
}
|
|
1208
|
-
}
|
|
1209
|
-
|
|
1210
|
-
/**
|
|
1211
|
-
* Get a block header specified by its archive root.
|
|
1212
|
-
* @param archive - The archive root being requested.
|
|
1213
|
-
* @returns The requested block header.
|
|
1214
|
-
*/
|
|
1215
|
-
public async getBlockHeaderByArchive(archive: Fr): Promise<BlockHeader | undefined> {
|
|
1216
|
-
return await this.blockSource.getBlockHeaderByArchive(archive);
|
|
1217
|
-
}
|
|
1218
|
-
|
|
1219
|
-
public getBlockData(number: BlockNumber): Promise<BlockData | undefined> {
|
|
1220
|
-
return this.blockSource.getBlockData(number);
|
|
1221
|
-
}
|
|
1222
|
-
|
|
1223
|
-
public getBlockDataByArchive(archive: Fr): Promise<BlockData | undefined> {
|
|
1224
|
-
return this.blockSource.getBlockDataByArchive(archive);
|
|
1225
|
-
}
|
|
1226
|
-
|
|
1227
1481
|
/**
|
|
1228
1482
|
* Simulates the public part of a transaction with the current state.
|
|
1229
1483
|
* @param tx - The transaction to simulate.
|
|
@@ -1315,7 +1569,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1315
1569
|
{ isSimulation, skipFeeEnforcement }: { isSimulation?: boolean; skipFeeEnforcement?: boolean } = {},
|
|
1316
1570
|
): Promise<TxValidationResult> {
|
|
1317
1571
|
const db = this.worldStateSynchronizer.getCommitted();
|
|
1318
|
-
const verifier = isSimulation ? undefined : this.
|
|
1572
|
+
const verifier = isSimulation ? undefined : this.rpcProofVerifier;
|
|
1319
1573
|
|
|
1320
1574
|
// We accept transactions if they are not expired by the next slot (checked based on the ExpirationTimestamp field)
|
|
1321
1575
|
const { ts: nextSlotTimestamp } = this.epochCache.getEpochAndSlotInNextL1Slot();
|
|
@@ -1364,7 +1618,15 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1364
1618
|
archiver.updateConfig(config);
|
|
1365
1619
|
}
|
|
1366
1620
|
if (newConfig.realProofs !== this.config.realProofs) {
|
|
1367
|
-
|
|
1621
|
+
await Promise.all([tryStop(this.peerProofVerifier), tryStop(this.rpcProofVerifier)]);
|
|
1622
|
+
if (newConfig.realProofs) {
|
|
1623
|
+
this.peerProofVerifier = await BatchChonkVerifier.new(newConfig, newConfig.bbChonkVerifyMaxBatch, 'peer');
|
|
1624
|
+
const rpcVerifier = await BBCircuitVerifier.new(newConfig);
|
|
1625
|
+
this.rpcProofVerifier = new QueuedIVCVerifier(rpcVerifier, newConfig.numConcurrentIVCVerifiers);
|
|
1626
|
+
} else {
|
|
1627
|
+
this.peerProofVerifier = new TestCircuitVerifier();
|
|
1628
|
+
this.rpcProofVerifier = new TestCircuitVerifier();
|
|
1629
|
+
}
|
|
1368
1630
|
}
|
|
1369
1631
|
|
|
1370
1632
|
this.config = newConfig;
|
|
@@ -1439,7 +1701,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1439
1701
|
return Promise.resolve();
|
|
1440
1702
|
}
|
|
1441
1703
|
|
|
1442
|
-
public async rollbackTo(targetBlock: BlockNumber, force?: boolean): Promise<void> {
|
|
1704
|
+
public async rollbackTo(targetBlock: BlockNumber, force?: boolean, resumeSync = true): Promise<void> {
|
|
1443
1705
|
const archiver = this.blockSource as Archiver;
|
|
1444
1706
|
if (!('rollbackTo' in archiver)) {
|
|
1445
1707
|
throw new Error('Archiver implementation does not support rollbacks.');
|
|
@@ -1469,9 +1731,13 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1469
1731
|
this.log.error(`Error during rollback`, err);
|
|
1470
1732
|
throw err;
|
|
1471
1733
|
} finally {
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1734
|
+
if (resumeSync) {
|
|
1735
|
+
this.log.info(`Resuming world state and archiver sync.`);
|
|
1736
|
+
this.worldStateSynchronizer.resumeSync();
|
|
1737
|
+
archiver.resume();
|
|
1738
|
+
} else {
|
|
1739
|
+
this.log.info(`Sync left paused after rollback (resumeSync=false).`);
|
|
1740
|
+
}
|
|
1475
1741
|
}
|
|
1476
1742
|
}
|
|
1477
1743
|
|
|
@@ -1488,19 +1754,12 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1488
1754
|
return Promise.resolve();
|
|
1489
1755
|
}
|
|
1490
1756
|
|
|
1491
|
-
public getSlashPayloads(): Promise<SlashPayloadRound[]> {
|
|
1492
|
-
if (!this.slasherClient) {
|
|
1493
|
-
throw new Error(`Slasher client not enabled`);
|
|
1494
|
-
}
|
|
1495
|
-
return this.slasherClient.getSlashPayloads();
|
|
1496
|
-
}
|
|
1497
|
-
|
|
1498
1757
|
public getSlashOffenses(round: bigint | 'all' | 'current'): Promise<Offense[]> {
|
|
1499
1758
|
if (!this.slasherClient) {
|
|
1500
1759
|
throw new Error(`Slasher client not enabled`);
|
|
1501
1760
|
}
|
|
1502
1761
|
if (round === 'all') {
|
|
1503
|
-
return this.slasherClient.
|
|
1762
|
+
return this.slasherClient.getOffenses();
|
|
1504
1763
|
} else {
|
|
1505
1764
|
return this.slasherClient.gatherOffensesForRound(round === 'current' ? undefined : BigInt(round));
|
|
1506
1765
|
}
|
|
@@ -1594,6 +1853,40 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1594
1853
|
this.log.info('Keystore reloaded: coinbase, feeRecipient, and attester keys updated');
|
|
1595
1854
|
}
|
|
1596
1855
|
|
|
1856
|
+
public async mineBlock(): Promise<void> {
|
|
1857
|
+
if (!this.sequencer) {
|
|
1858
|
+
throw new BadRequestError('Cannot mine block: no sequencer is running');
|
|
1859
|
+
}
|
|
1860
|
+
|
|
1861
|
+
const currentBlockNumber = await this.getBlockNumber();
|
|
1862
|
+
|
|
1863
|
+
// Use slot duration + 50% buffer as the timeout so this works on running networks too
|
|
1864
|
+
const { slotDuration } = await this.blockSource.getL1Constants();
|
|
1865
|
+
const timeoutSeconds = Math.ceil(slotDuration * 1.5);
|
|
1866
|
+
|
|
1867
|
+
// Temporarily set minTxsPerBlock to 0 so the sequencer produces a block even with no txs
|
|
1868
|
+
const originalMinTxsPerBlock = this.sequencer.getSequencer().getConfig().minTxsPerBlock;
|
|
1869
|
+
this.sequencer.updateConfig({ minTxsPerBlock: 0 });
|
|
1870
|
+
|
|
1871
|
+
try {
|
|
1872
|
+
// Trigger the sequencer to produce a block immediately
|
|
1873
|
+
void this.sequencer.trigger();
|
|
1874
|
+
|
|
1875
|
+
// Wait for the new L2 block to appear
|
|
1876
|
+
await retryUntil(
|
|
1877
|
+
async () => {
|
|
1878
|
+
const newBlockNumber = await this.getBlockNumber();
|
|
1879
|
+
return newBlockNumber > currentBlockNumber ? true : undefined;
|
|
1880
|
+
},
|
|
1881
|
+
'mineBlock',
|
|
1882
|
+
timeoutSeconds,
|
|
1883
|
+
0.1,
|
|
1884
|
+
);
|
|
1885
|
+
} finally {
|
|
1886
|
+
this.sequencer.updateConfig({ minTxsPerBlock: originalMinTxsPerBlock });
|
|
1887
|
+
}
|
|
1888
|
+
}
|
|
1889
|
+
|
|
1597
1890
|
#getInitialHeaderHash(): Promise<BlockHash> {
|
|
1598
1891
|
if (!this.initialHeaderHashPromise) {
|
|
1599
1892
|
this.initialHeaderHashPromise = this.worldStateSynchronizer.getCommitted().getInitialHeader().hash();
|
|
@@ -1625,25 +1918,28 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1625
1918
|
if (BlockHash.isBlockHash(block)) {
|
|
1626
1919
|
const initialBlockHash = await this.#getInitialHeaderHash();
|
|
1627
1920
|
if (block.equals(initialBlockHash)) {
|
|
1628
|
-
// Block
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1921
|
+
// Block 0 is a first-class historical block: its state lives in the trees' persisted
|
|
1922
|
+
// block-0 payload. Resolving the genesis hash to block number 0 lets the snapshot path
|
|
1923
|
+
// pin reads to genesis state even after the node has advanced past it.
|
|
1924
|
+
blockNumber = BlockNumber.ZERO;
|
|
1925
|
+
} else {
|
|
1926
|
+
const header = await this.blockSource.getBlockHeaderByHash(block);
|
|
1927
|
+
if (!header) {
|
|
1928
|
+
throw new Error(
|
|
1929
|
+
`Block hash ${block.toString()} not found when querying world state. If the node API has been queried with anchor block hash possibly a reorg has occurred.`,
|
|
1930
|
+
);
|
|
1931
|
+
}
|
|
1932
|
+
blockNumber = header.getBlockNumber();
|
|
1637
1933
|
}
|
|
1638
|
-
|
|
1639
|
-
blockNumber = header.getBlockNumber();
|
|
1640
1934
|
} else {
|
|
1641
1935
|
blockNumber = block as BlockNumber;
|
|
1642
1936
|
}
|
|
1643
1937
|
|
|
1644
1938
|
// Check it's within world state sync range
|
|
1645
1939
|
if (blockNumber > blockSyncedTo) {
|
|
1646
|
-
throw new Error(
|
|
1940
|
+
throw new Error(
|
|
1941
|
+
`Queried block ${inspectBlockParameter(block)} not yet synced by the node (node is synced upto ${blockSyncedTo}).`,
|
|
1942
|
+
);
|
|
1647
1943
|
}
|
|
1648
1944
|
this.log.debug(`Using snapshot for block ${blockNumber}, world state synced upto ${blockSyncedTo}`);
|
|
1649
1945
|
|
|
@@ -1652,9 +1948,10 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1652
1948
|
// Double-check world-state synced to the same block hash as was requested
|
|
1653
1949
|
if (BlockHash.isBlockHash(block)) {
|
|
1654
1950
|
const blockHash = await snapshot.getLeafValue(MerkleTreeId.ARCHIVE, BigInt(blockNumber));
|
|
1655
|
-
if (!blockHash || !
|
|
1951
|
+
if (!blockHash || !block.equals(blockHash)) {
|
|
1952
|
+
const initialBlockHash = await this.#getInitialHeaderHash();
|
|
1656
1953
|
throw new Error(
|
|
1657
|
-
`Block hash ${block.toString()} not found in world state at block number ${blockNumber}. If the node API has been queried with anchor block hash possibly a reorg has occurred.`,
|
|
1954
|
+
`Block hash ${block.toString()} not found in world state at block number ${blockNumber} (world state has ${blockHash?.toString() ?? 'no hash'} at that index, genesis header hash is ${initialBlockHash.toString()}). If the node API has been queried with anchor block hash possibly a reorg has occurred.`,
|
|
1658
1955
|
);
|
|
1659
1956
|
}
|
|
1660
1957
|
}
|
|
@@ -1662,23 +1959,31 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1662
1959
|
return snapshot;
|
|
1663
1960
|
}
|
|
1664
1961
|
|
|
1665
|
-
/** Resolves
|
|
1962
|
+
/** Resolves any {@link BlockParameter} variant to a concrete block number. */
|
|
1666
1963
|
protected async resolveBlockNumber(block: BlockParameter): Promise<BlockNumber> {
|
|
1667
|
-
|
|
1668
|
-
|
|
1964
|
+
const resolved = await this.resolveBlockParameter(block);
|
|
1965
|
+
if (resolved.number !== undefined) {
|
|
1966
|
+
return resolved.number;
|
|
1669
1967
|
}
|
|
1670
|
-
if (
|
|
1968
|
+
if (resolved.hash !== undefined) {
|
|
1671
1969
|
const initialBlockHash = await this.#getInitialHeaderHash();
|
|
1672
|
-
if (
|
|
1970
|
+
if (resolved.hash.equals(initialBlockHash)) {
|
|
1673
1971
|
return BlockNumber.ZERO;
|
|
1674
1972
|
}
|
|
1675
|
-
const header = await this.blockSource.getBlockHeaderByHash(
|
|
1973
|
+
const header = await this.blockSource.getBlockHeaderByHash(resolved.hash);
|
|
1974
|
+
if (!header) {
|
|
1975
|
+
throw new Error(`Block hash ${resolved.hash.toString()} not found.`);
|
|
1976
|
+
}
|
|
1977
|
+
return header.getBlockNumber();
|
|
1978
|
+
}
|
|
1979
|
+
if (resolved.archive !== undefined) {
|
|
1980
|
+
const header = await this.blockSource.getBlockHeaderByArchive(resolved.archive);
|
|
1676
1981
|
if (!header) {
|
|
1677
|
-
throw new Error(`Block
|
|
1982
|
+
throw new Error(`Block with archive ${resolved.archive.toString()} not found.`);
|
|
1678
1983
|
}
|
|
1679
1984
|
return header.getBlockNumber();
|
|
1680
1985
|
}
|
|
1681
|
-
|
|
1986
|
+
throw new BadRequestError(`Invalid BlockParameter: ${JSON.stringify(block)}`);
|
|
1682
1987
|
}
|
|
1683
1988
|
|
|
1684
1989
|
/**
|