@aztec/aztec-node 4.0.0-devnet.2-patch.4 → 4.0.0-devnet.3-patch.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dest/aztec-node/server.d.ts +18 -21
- package/dest/aztec-node/server.d.ts.map +1 -1
- package/dest/aztec-node/server.js +220 -152
- package/dest/sentinel/sentinel.js +3 -3
- package/package.json +27 -27
- package/src/aztec-node/server.ts +253 -156
- package/src/sentinel/sentinel.ts +3 -3
package/src/aztec-node/server.ts
CHANGED
|
@@ -5,11 +5,11 @@ import { Blob } from '@aztec/blob-lib';
|
|
|
5
5
|
import { ARCHIVE_HEIGHT, type L1_TO_L2_MSG_TREE_HEIGHT, type NOTE_HASH_TREE_HEIGHT } from '@aztec/constants';
|
|
6
6
|
import { EpochCache, type EpochCacheInterface } from '@aztec/epoch-cache';
|
|
7
7
|
import { createEthereumChain } from '@aztec/ethereum/chain';
|
|
8
|
-
import { getPublicClient } from '@aztec/ethereum/client';
|
|
8
|
+
import { getPublicClient, makeL1HttpTransport } from '@aztec/ethereum/client';
|
|
9
9
|
import { RegistryContract, RollupContract } from '@aztec/ethereum/contracts';
|
|
10
10
|
import type { L1ContractAddresses } from '@aztec/ethereum/l1-contract-addresses';
|
|
11
11
|
import { BlockNumber, CheckpointNumber, EpochNumber, SlotNumber } from '@aztec/foundation/branded-types';
|
|
12
|
-
import { compactArray, pick, unique } from '@aztec/foundation/collection';
|
|
12
|
+
import { chunkBy, compactArray, pick, unique } from '@aztec/foundation/collection';
|
|
13
13
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
14
14
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
15
15
|
import { BadRequestError } from '@aztec/foundation/json-rpc';
|
|
@@ -20,7 +20,13 @@ import { MembershipWitness, SiblingPath } from '@aztec/foundation/trees';
|
|
|
20
20
|
import { type KeyStore, KeystoreManager, loadKeystores, mergeKeystores } from '@aztec/node-keystore';
|
|
21
21
|
import { trySnapshotSync, uploadSnapshot } from '@aztec/node-lib/actions';
|
|
22
22
|
import { createForwarderL1TxUtilsFromSigners, createL1TxUtilsFromSigners } from '@aztec/node-lib/factories';
|
|
23
|
-
import {
|
|
23
|
+
import {
|
|
24
|
+
type P2P,
|
|
25
|
+
type P2PClientDeps,
|
|
26
|
+
createP2PClient,
|
|
27
|
+
createTxValidatorForAcceptingTxsOverRPC,
|
|
28
|
+
getDefaultAllowedSetupFunctions,
|
|
29
|
+
} from '@aztec/p2p';
|
|
24
30
|
import { ProtocolContractAddress } from '@aztec/protocol-contracts';
|
|
25
31
|
import { type ProverNode, type ProverNodeDeps, createProverNode } from '@aztec/prover-node';
|
|
26
32
|
import { createKeyStoreForProver } from '@aztec/prover-node/config';
|
|
@@ -70,9 +76,9 @@ import {
|
|
|
70
76
|
type WorldStateSynchronizer,
|
|
71
77
|
tryStop,
|
|
72
78
|
} from '@aztec/stdlib/interfaces/server';
|
|
73
|
-
import type { LogFilter, SiloedTag, Tag, TxScopedL2Log } from '@aztec/stdlib/logs';
|
|
79
|
+
import type { DebugLogStore, LogFilter, SiloedTag, Tag, TxScopedL2Log } from '@aztec/stdlib/logs';
|
|
80
|
+
import { InMemoryDebugLogStore, NullDebugLogStore } from '@aztec/stdlib/logs';
|
|
74
81
|
import { InboxLeaf, type L1ToL2MessageSource } from '@aztec/stdlib/messaging';
|
|
75
|
-
import { P2PClientType } from '@aztec/stdlib/p2p';
|
|
76
82
|
import type { Offense, SlashPayloadRound } from '@aztec/stdlib/slashing';
|
|
77
83
|
import type { NullifierLeafPreimage, PublicDataTreeLeaf, PublicDataTreeLeafPreimage } from '@aztec/stdlib/trees';
|
|
78
84
|
import { MerkleTreeId, NullifierMembershipWitness, PublicDataWitness } from '@aztec/stdlib/trees';
|
|
@@ -102,13 +108,13 @@ import {
|
|
|
102
108
|
FullNodeCheckpointsBuilder,
|
|
103
109
|
NodeKeystoreAdapter,
|
|
104
110
|
ValidatorClient,
|
|
105
|
-
|
|
111
|
+
createProposalHandler,
|
|
106
112
|
createValidatorClient,
|
|
107
|
-
createValidatorForAcceptingTxs,
|
|
108
113
|
} from '@aztec/validator-client';
|
|
114
|
+
import type { SlashingProtectionDatabase } from '@aztec/validator-ha-signer/types';
|
|
109
115
|
import { createWorldStateSynchronizer } from '@aztec/world-state';
|
|
110
116
|
|
|
111
|
-
import { createPublicClient
|
|
117
|
+
import { createPublicClient } from 'viem';
|
|
112
118
|
|
|
113
119
|
import { createSentinel } from '../sentinel/factory.js';
|
|
114
120
|
import { Sentinel } from '../sentinel/sentinel.js';
|
|
@@ -151,12 +157,20 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
151
157
|
private blobClient?: BlobClientInterface,
|
|
152
158
|
private validatorClient?: ValidatorClient,
|
|
153
159
|
private keyStoreManager?: KeystoreManager,
|
|
160
|
+
private debugLogStore: DebugLogStore = new NullDebugLogStore(),
|
|
154
161
|
) {
|
|
155
162
|
this.metrics = new NodeMetrics(telemetry, 'AztecNodeService');
|
|
156
163
|
this.tracer = telemetry.getTracer('AztecNodeService');
|
|
157
164
|
|
|
158
165
|
this.log.info(`Aztec Node version: ${this.packageVersion}`);
|
|
159
166
|
this.log.info(`Aztec Node started on chain 0x${l1ChainId.toString(16)}`, config.l1Contracts);
|
|
167
|
+
|
|
168
|
+
// A defensive check that protects us against introducing a bug in the complex `createAndSync` function. We must
|
|
169
|
+
// never have debugLogStore enabled when not in test mode because then we would be accumulating debug logs in
|
|
170
|
+
// memory which could be a DoS vector on the sequencer (since no fees are paid for debug logs).
|
|
171
|
+
if (debugLogStore.isEnabled && config.realProofs) {
|
|
172
|
+
throw new Error('debugLogStore should never be enabled when realProofs are set');
|
|
173
|
+
}
|
|
160
174
|
}
|
|
161
175
|
|
|
162
176
|
public async getWorldStateSyncStatus(): Promise<WorldStateSyncStatus> {
|
|
@@ -180,8 +194,9 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
180
194
|
logger?: Logger;
|
|
181
195
|
publisher?: SequencerPublisher;
|
|
182
196
|
dateProvider?: DateProvider;
|
|
183
|
-
p2pClientDeps?: P2PClientDeps
|
|
197
|
+
p2pClientDeps?: P2PClientDeps;
|
|
184
198
|
proverNodeDeps?: Partial<ProverNodeDeps>;
|
|
199
|
+
slashingProtectionDb?: SlashingProtectionDatabase;
|
|
185
200
|
} = {},
|
|
186
201
|
options: {
|
|
187
202
|
prefilledPublicData?: PublicDataTreeLeaf[];
|
|
@@ -244,7 +259,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
244
259
|
|
|
245
260
|
const publicClient = createPublicClient({
|
|
246
261
|
chain: ethereumChain.chainInfo,
|
|
247
|
-
transport:
|
|
262
|
+
transport: makeL1HttpTransport(config.l1RpcUrls, { timeout: config.l1HttpTimeoutMS }),
|
|
248
263
|
pollingInterval: config.viemPollingIntervalMS,
|
|
249
264
|
});
|
|
250
265
|
|
|
@@ -258,10 +273,11 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
258
273
|
config.l1Contracts = { ...config.l1Contracts, ...l1ContractsAddresses };
|
|
259
274
|
|
|
260
275
|
const rollupContract = new RollupContract(publicClient, config.l1Contracts.rollupAddress.toString());
|
|
261
|
-
const [l1GenesisTime, slotDuration, rollupVersionFromRollup] = await Promise.all([
|
|
276
|
+
const [l1GenesisTime, slotDuration, rollupVersionFromRollup, rollupManaLimit] = await Promise.all([
|
|
262
277
|
rollupContract.getL1GenesisTime(),
|
|
263
278
|
rollupContract.getSlotDuration(),
|
|
264
279
|
rollupContract.getVersion(),
|
|
280
|
+
rollupContract.getManaLimit().then(Number),
|
|
265
281
|
] as const);
|
|
266
282
|
|
|
267
283
|
config.rollupVersion ??= Number(rollupVersionFromRollup);
|
|
@@ -296,14 +312,28 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
296
312
|
config.realProofs || config.debugForceTxProofVerification
|
|
297
313
|
? await BBCircuitVerifier.new(config)
|
|
298
314
|
: new TestCircuitVerifier(config.proverTestVerificationDelayMs);
|
|
315
|
+
|
|
316
|
+
let debugLogStore: DebugLogStore;
|
|
299
317
|
if (!config.realProofs) {
|
|
300
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',
|
|
323
|
+
);
|
|
324
|
+
} else {
|
|
325
|
+
debugLogStore = new NullDebugLogStore();
|
|
301
326
|
}
|
|
327
|
+
|
|
302
328
|
const proofVerifier = new QueuedIVCVerifier(config, circuitVerifier);
|
|
303
329
|
|
|
330
|
+
const proverOnly = config.enableProverNode && config.disableValidator;
|
|
331
|
+
if (proverOnly) {
|
|
332
|
+
log.info('Starting in prover-only mode: skipping validator, sequencer, sentinel, and slasher subsystems');
|
|
333
|
+
}
|
|
334
|
+
|
|
304
335
|
// create the tx pool and the p2p client, which will need the l2 block source
|
|
305
336
|
const p2pClient = await createP2PClient(
|
|
306
|
-
P2PClientType.Full,
|
|
307
337
|
config,
|
|
308
338
|
archiver,
|
|
309
339
|
proofVerifier,
|
|
@@ -315,59 +345,72 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
315
345
|
deps.p2pClientDeps,
|
|
316
346
|
);
|
|
317
347
|
|
|
318
|
-
// We
|
|
319
|
-
|
|
348
|
+
// We'll accumulate sentinel watchers here
|
|
349
|
+
const watchers: Watcher[] = [];
|
|
320
350
|
|
|
321
|
-
// Create FullNodeCheckpointsBuilder for
|
|
351
|
+
// Create FullNodeCheckpointsBuilder for block proposal handling and tx validation.
|
|
352
|
+
// Override maxTxsPerCheckpoint with the validator-specific limit if set.
|
|
322
353
|
const validatorCheckpointsBuilder = new FullNodeCheckpointsBuilder(
|
|
323
|
-
{
|
|
354
|
+
{
|
|
355
|
+
...config,
|
|
356
|
+
l1GenesisTime,
|
|
357
|
+
slotDuration: Number(slotDuration),
|
|
358
|
+
rollupManaLimit,
|
|
359
|
+
maxTxsPerCheckpoint: config.validateMaxTxsPerCheckpoint,
|
|
360
|
+
},
|
|
324
361
|
worldStateSynchronizer,
|
|
325
362
|
archiver,
|
|
326
363
|
dateProvider,
|
|
327
364
|
telemetry,
|
|
328
365
|
);
|
|
329
366
|
|
|
330
|
-
|
|
331
|
-
const watchers: Watcher[] = [];
|
|
367
|
+
let validatorClient: ValidatorClient | undefined;
|
|
332
368
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
369
|
+
if (!proverOnly) {
|
|
370
|
+
// Create validator client if required
|
|
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
|
+
});
|
|
346
384
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
385
|
+
// If we have a validator client, register it as a source of offenses for the slasher,
|
|
386
|
+
// and have it register callbacks on the p2p client *before* we start it, otherwise messages
|
|
387
|
+
// like attestations or auths will fail.
|
|
388
|
+
if (validatorClient) {
|
|
389
|
+
watchers.push(validatorClient);
|
|
390
|
+
if (!options.dontStartSequencer) {
|
|
391
|
+
await validatorClient.registerHandlers();
|
|
392
|
+
}
|
|
354
393
|
}
|
|
355
394
|
}
|
|
356
395
|
|
|
357
|
-
// If there's no validator client
|
|
358
|
-
//
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
396
|
+
// If there's no validator client, create a ProposalHandler to handle block and checkpoint proposals
|
|
397
|
+
// for monitoring or reexecution. Reexecution (default) allows us to follow the pending chain,
|
|
398
|
+
// while non-reexecution is used for validating the proposals and collecting their txs.
|
|
399
|
+
// Checkpoint proposals are handled if the blob client can upload blobs.
|
|
400
|
+
if (!validatorClient) {
|
|
401
|
+
const reexecute = !!config.alwaysReexecuteBlockProposals;
|
|
402
|
+
log.info(`Setting up proposal handler` + (reexecute ? ' with reexecution of proposals' : ''));
|
|
403
|
+
createProposalHandler(config, {
|
|
362
404
|
checkpointsBuilder: validatorCheckpointsBuilder,
|
|
363
405
|
worldState: worldStateSynchronizer,
|
|
364
406
|
epochCache,
|
|
365
407
|
blockSource: archiver,
|
|
366
408
|
l1ToL2MessageSource: archiver,
|
|
367
409
|
p2pClient,
|
|
410
|
+
blobClient,
|
|
368
411
|
dateProvider,
|
|
369
412
|
telemetry,
|
|
370
|
-
}).
|
|
413
|
+
}).register(p2pClient, reexecute);
|
|
371
414
|
}
|
|
372
415
|
|
|
373
416
|
// Start world state and wait for it to sync to the archiver.
|
|
@@ -376,29 +419,33 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
376
419
|
// Start p2p. Note that it depends on world state to be running.
|
|
377
420
|
await p2pClient.start();
|
|
378
421
|
|
|
379
|
-
|
|
380
|
-
if (validatorsSentinel && config.slashInactivityPenalty > 0n) {
|
|
381
|
-
watchers.push(validatorsSentinel);
|
|
382
|
-
}
|
|
383
|
-
|
|
422
|
+
let validatorsSentinel: Awaited<ReturnType<typeof createSentinel>> | undefined;
|
|
384
423
|
let epochPruneWatcher: EpochPruneWatcher | undefined;
|
|
385
|
-
if (config.slashPrunePenalty > 0n || config.slashDataWithholdingPenalty > 0n) {
|
|
386
|
-
epochPruneWatcher = new EpochPruneWatcher(
|
|
387
|
-
archiver,
|
|
388
|
-
archiver,
|
|
389
|
-
epochCache,
|
|
390
|
-
p2pClient.getTxProvider(),
|
|
391
|
-
validatorCheckpointsBuilder,
|
|
392
|
-
config,
|
|
393
|
-
);
|
|
394
|
-
watchers.push(epochPruneWatcher);
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
// We assume we want to slash for invalid attestations unless all max penalties are set to 0
|
|
398
424
|
let attestationsBlockWatcher: AttestationsBlockWatcher | undefined;
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
425
|
+
|
|
426
|
+
if (!proverOnly) {
|
|
427
|
+
validatorsSentinel = await createSentinel(epochCache, archiver, p2pClient, config);
|
|
428
|
+
if (validatorsSentinel && config.slashInactivityPenalty > 0n) {
|
|
429
|
+
watchers.push(validatorsSentinel);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
if (config.slashPrunePenalty > 0n || config.slashDataWithholdingPenalty > 0n) {
|
|
433
|
+
epochPruneWatcher = new EpochPruneWatcher(
|
|
434
|
+
archiver,
|
|
435
|
+
archiver,
|
|
436
|
+
epochCache,
|
|
437
|
+
p2pClient.getTxProvider(),
|
|
438
|
+
validatorCheckpointsBuilder,
|
|
439
|
+
config,
|
|
440
|
+
);
|
|
441
|
+
watchers.push(epochPruneWatcher);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// We assume we want to slash for invalid attestations unless all max penalties are set to 0
|
|
445
|
+
if (config.slashProposeInvalidAttestationsPenalty > 0n || config.slashAttestDescendantOfInvalidPenalty > 0n) {
|
|
446
|
+
attestationsBlockWatcher = new AttestationsBlockWatcher(archiver, epochCache, config);
|
|
447
|
+
watchers.push(attestationsBlockWatcher);
|
|
448
|
+
}
|
|
402
449
|
}
|
|
403
450
|
|
|
404
451
|
// Start p2p-related services once the archiver has completed sync
|
|
@@ -413,6 +460,14 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
413
460
|
})
|
|
414
461
|
.catch(err => log.error('Failed to start p2p services after archiver sync', err));
|
|
415
462
|
|
|
463
|
+
const globalVariableBuilder = new GlobalVariableBuilder(dateProvider, publicClient, {
|
|
464
|
+
l1Contracts: config.l1Contracts,
|
|
465
|
+
ethereumSlotDuration: config.ethereumSlotDuration,
|
|
466
|
+
rollupVersion: BigInt(config.rollupVersion),
|
|
467
|
+
l1GenesisTime,
|
|
468
|
+
slotDuration: Number(slotDuration),
|
|
469
|
+
});
|
|
470
|
+
|
|
416
471
|
// Validator enabled, create/start relevant service
|
|
417
472
|
let sequencer: SequencerClient | undefined;
|
|
418
473
|
let slasherClient: SlasherClientInterface | undefined;
|
|
@@ -452,11 +507,12 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
452
507
|
|
|
453
508
|
// Create and start the sequencer client
|
|
454
509
|
const checkpointsBuilder = new CheckpointsBuilder(
|
|
455
|
-
{ ...config, l1GenesisTime, slotDuration: Number(slotDuration) },
|
|
510
|
+
{ ...config, l1GenesisTime, slotDuration: Number(slotDuration), rollupManaLimit },
|
|
456
511
|
worldStateSynchronizer,
|
|
457
512
|
archiver,
|
|
458
513
|
dateProvider,
|
|
459
514
|
telemetry,
|
|
515
|
+
debugLogStore,
|
|
460
516
|
);
|
|
461
517
|
|
|
462
518
|
sequencer = await SequencerClient.new(config, {
|
|
@@ -474,6 +530,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
474
530
|
dateProvider,
|
|
475
531
|
blobClient,
|
|
476
532
|
nodeKeyStore: keyStoreManager!,
|
|
533
|
+
globalVariableBuilder,
|
|
477
534
|
});
|
|
478
535
|
}
|
|
479
536
|
|
|
@@ -507,13 +564,6 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
507
564
|
}
|
|
508
565
|
}
|
|
509
566
|
|
|
510
|
-
const globalVariableBuilder = new GlobalVariableBuilder({
|
|
511
|
-
...config,
|
|
512
|
-
rollupVersion: BigInt(config.rollupVersion),
|
|
513
|
-
l1GenesisTime,
|
|
514
|
-
slotDuration: Number(slotDuration),
|
|
515
|
-
});
|
|
516
|
-
|
|
517
567
|
const node = new AztecNodeService(
|
|
518
568
|
config,
|
|
519
569
|
p2pClient,
|
|
@@ -538,6 +588,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
538
588
|
blobClient,
|
|
539
589
|
validatorClient,
|
|
540
590
|
keyStoreManager,
|
|
591
|
+
debugLogStore,
|
|
541
592
|
);
|
|
542
593
|
|
|
543
594
|
return node;
|
|
@@ -581,7 +632,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
581
632
|
}
|
|
582
633
|
|
|
583
634
|
public async getAllowedPublicSetup(): Promise<AllowedElement[]> {
|
|
584
|
-
return this.config.
|
|
635
|
+
return [...(await getDefaultAllowedSetupFunctions()), ...(this.config.txPublicSetupAllowListExtend ?? [])];
|
|
585
636
|
}
|
|
586
637
|
|
|
587
638
|
/**
|
|
@@ -676,6 +727,10 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
676
727
|
return (await this.blockSource.getCheckpointedBlocks(from, limit)) ?? [];
|
|
677
728
|
}
|
|
678
729
|
|
|
730
|
+
public getCheckpointsDataForEpoch(epochNumber: EpochNumber) {
|
|
731
|
+
return this.blockSource.getCheckpointsDataForEpoch(epochNumber);
|
|
732
|
+
}
|
|
733
|
+
|
|
679
734
|
/**
|
|
680
735
|
* Method to fetch the current min L2 fees.
|
|
681
736
|
* @returns The current min L2 fees.
|
|
@@ -708,6 +763,10 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
708
763
|
return await this.blockSource.getCheckpointedL2BlockNumber();
|
|
709
764
|
}
|
|
710
765
|
|
|
766
|
+
public getCheckpointNumber(): Promise<CheckpointNumber> {
|
|
767
|
+
return this.blockSource.getCheckpointNumber();
|
|
768
|
+
}
|
|
769
|
+
|
|
711
770
|
/**
|
|
712
771
|
* Method to fetch the version of the package.
|
|
713
772
|
* @returns The node package version
|
|
@@ -818,8 +877,9 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
818
877
|
}
|
|
819
878
|
|
|
820
879
|
await this.p2pClient!.sendTx(tx);
|
|
821
|
-
|
|
822
|
-
this.
|
|
880
|
+
const duration = timer.ms();
|
|
881
|
+
this.metrics.receivedTx(duration, true);
|
|
882
|
+
this.log.info(`Received tx ${txHash} in ${duration}ms`, { txHash });
|
|
823
883
|
}
|
|
824
884
|
|
|
825
885
|
public async getTxReceipt(txHash: TxHash): Promise<TxReceipt> {
|
|
@@ -831,18 +891,22 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
831
891
|
// Then get the actual tx from the archiver, which tracks every tx in a mined block.
|
|
832
892
|
const settledTxReceipt = await this.blockSource.getSettledTxReceipt(txHash);
|
|
833
893
|
|
|
894
|
+
let receipt: TxReceipt;
|
|
834
895
|
if (settledTxReceipt) {
|
|
835
|
-
|
|
836
|
-
return settledTxReceipt;
|
|
896
|
+
receipt = settledTxReceipt;
|
|
837
897
|
} else if (isKnownToPool) {
|
|
838
898
|
// If the tx is in the pool but not in the archiver, it's pending.
|
|
839
899
|
// This handles race conditions between archiver and p2p, where the archiver
|
|
840
900
|
// has pruned the block in which a tx was mined, but p2p has not caught up yet.
|
|
841
|
-
|
|
901
|
+
receipt = new TxReceipt(txHash, TxStatus.PENDING, undefined, undefined);
|
|
842
902
|
} else {
|
|
843
903
|
// Otherwise, if we don't know the tx, we consider it dropped.
|
|
844
|
-
|
|
904
|
+
receipt = new TxReceipt(txHash, TxStatus.DROPPED, undefined, 'Tx dropped by P2P node');
|
|
845
905
|
}
|
|
906
|
+
|
|
907
|
+
this.debugLogStore.decorateReceiptWithLogs(txHash.toString(), receipt);
|
|
908
|
+
|
|
909
|
+
return receipt;
|
|
846
910
|
}
|
|
847
911
|
|
|
848
912
|
public getTxEffect(txHash: TxHash): Promise<IndexedTxEffect | undefined> {
|
|
@@ -913,58 +977,64 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
913
977
|
treeId: MerkleTreeId,
|
|
914
978
|
leafValues: Fr[],
|
|
915
979
|
): Promise<(DataInBlock<bigint> | undefined)[]> {
|
|
916
|
-
const committedDb = await this
|
|
980
|
+
const committedDb = await this.getWorldState(referenceBlock);
|
|
917
981
|
const maybeIndices = await committedDb.findLeafIndices(
|
|
918
982
|
treeId,
|
|
919
983
|
leafValues.map(x => x.toBuffer()),
|
|
920
984
|
);
|
|
921
|
-
//
|
|
922
|
-
const
|
|
985
|
+
// Filter out undefined values to query block numbers only for found leaves
|
|
986
|
+
const definedIndices = maybeIndices.filter(x => x !== undefined);
|
|
923
987
|
|
|
924
|
-
// Now we find the block numbers for the indices
|
|
925
|
-
const blockNumbers = await committedDb.getBlockNumbersForLeafIndices(treeId,
|
|
988
|
+
// Now we find the block numbers for the defined indices
|
|
989
|
+
const blockNumbers = await committedDb.getBlockNumbersForLeafIndices(treeId, definedIndices);
|
|
926
990
|
|
|
927
|
-
//
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
991
|
+
// Build a map from leaf index to block number
|
|
992
|
+
const indexToBlockNumber = new Map<bigint, BlockNumber>();
|
|
993
|
+
for (let i = 0; i < definedIndices.length; i++) {
|
|
994
|
+
const blockNumber = blockNumbers[i];
|
|
995
|
+
if (blockNumber === undefined) {
|
|
996
|
+
throw new Error(
|
|
997
|
+
`Block number is undefined for leaf index ${definedIndices[i]} in tree ${MerkleTreeId[treeId]}`,
|
|
998
|
+
);
|
|
931
999
|
}
|
|
1000
|
+
indexToBlockNumber.set(definedIndices[i], blockNumber);
|
|
932
1001
|
}
|
|
933
1002
|
|
|
934
1003
|
// Get unique block numbers in order to optimize num calls to getLeafValue function.
|
|
935
|
-
const uniqueBlockNumbers = [...new Set(
|
|
1004
|
+
const uniqueBlockNumbers = [...new Set(indexToBlockNumber.values())];
|
|
936
1005
|
|
|
937
|
-
// Now we obtain the block hashes from the archive tree
|
|
938
|
-
// (note that block number corresponds to the leaf index in the archive tree).
|
|
1006
|
+
// Now we obtain the block hashes from the archive tree (block number = leaf index in archive tree).
|
|
939
1007
|
const blockHashes = await Promise.all(
|
|
940
1008
|
uniqueBlockNumbers.map(blockNumber => {
|
|
941
1009
|
return committedDb.getLeafValue(MerkleTreeId.ARCHIVE, BigInt(blockNumber));
|
|
942
1010
|
}),
|
|
943
1011
|
);
|
|
944
1012
|
|
|
945
|
-
//
|
|
1013
|
+
// Build a map from block number to block hash
|
|
1014
|
+
const blockNumberToHash = new Map<BlockNumber, Fr>();
|
|
946
1015
|
for (let i = 0; i < uniqueBlockNumbers.length; i++) {
|
|
947
|
-
|
|
1016
|
+
const blockHash = blockHashes[i];
|
|
1017
|
+
if (blockHash === undefined) {
|
|
948
1018
|
throw new Error(`Block hash is undefined for block number ${uniqueBlockNumbers[i]}`);
|
|
949
1019
|
}
|
|
1020
|
+
blockNumberToHash.set(uniqueBlockNumbers[i], blockHash);
|
|
950
1021
|
}
|
|
951
1022
|
|
|
952
1023
|
// Create DataInBlock objects by combining indices, blockNumbers and blockHashes and return them.
|
|
953
|
-
return maybeIndices.map(
|
|
1024
|
+
return maybeIndices.map(index => {
|
|
954
1025
|
if (index === undefined) {
|
|
955
1026
|
return undefined;
|
|
956
1027
|
}
|
|
957
|
-
const blockNumber =
|
|
1028
|
+
const blockNumber = indexToBlockNumber.get(index);
|
|
958
1029
|
if (blockNumber === undefined) {
|
|
959
|
-
|
|
1030
|
+
throw new Error(`Block number not found for leaf index ${index} in tree ${MerkleTreeId[treeId]}`);
|
|
960
1031
|
}
|
|
961
|
-
const
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
return undefined;
|
|
1032
|
+
const blockHash = blockNumberToHash.get(blockNumber);
|
|
1033
|
+
if (blockHash === undefined) {
|
|
1034
|
+
throw new Error(`Block hash not found for block number ${blockNumber}`);
|
|
965
1035
|
}
|
|
966
1036
|
return {
|
|
967
|
-
l2BlockNumber:
|
|
1037
|
+
l2BlockNumber: blockNumber,
|
|
968
1038
|
l2BlockHash: new BlockHash(blockHash),
|
|
969
1039
|
data: index,
|
|
970
1040
|
};
|
|
@@ -975,7 +1045,21 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
975
1045
|
referenceBlock: BlockParameter,
|
|
976
1046
|
blockHash: BlockHash,
|
|
977
1047
|
): Promise<MembershipWitness<typeof ARCHIVE_HEIGHT> | undefined> {
|
|
978
|
-
|
|
1048
|
+
// Block 0 (the initial block) has an empty archive, so no membership witness can exist.
|
|
1049
|
+
if (referenceBlock === BlockNumber.ZERO) {
|
|
1050
|
+
return undefined;
|
|
1051
|
+
}
|
|
1052
|
+
if (BlockHash.isBlockHash(referenceBlock)) {
|
|
1053
|
+
const initialBlockHash = await this.#getInitialHeaderHash();
|
|
1054
|
+
if (referenceBlock.equals(initialBlockHash)) {
|
|
1055
|
+
return undefined;
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
// The Noir circuit checks the archive membership proof against `anchor_block_header.last_archive.root`,
|
|
1059
|
+
// which is the archive tree root BEFORE the anchor block was added (i.e. the state after block N-1).
|
|
1060
|
+
// So we need the world state at block N-1, not block N, to produce a sibling path matching that root.
|
|
1061
|
+
const referenceBlockNumber = await this.resolveBlockNumber(referenceBlock);
|
|
1062
|
+
const committedDb = await this.getWorldState(BlockNumber(referenceBlockNumber - 1));
|
|
979
1063
|
const [pathAndIndex] = await committedDb.findSiblingPaths<MerkleTreeId.ARCHIVE>(MerkleTreeId.ARCHIVE, [blockHash]);
|
|
980
1064
|
return pathAndIndex === undefined
|
|
981
1065
|
? undefined
|
|
@@ -986,7 +1070,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
986
1070
|
referenceBlock: BlockParameter,
|
|
987
1071
|
noteHash: Fr,
|
|
988
1072
|
): Promise<MembershipWitness<typeof NOTE_HASH_TREE_HEIGHT> | undefined> {
|
|
989
|
-
const committedDb = await this
|
|
1073
|
+
const committedDb = await this.getWorldState(referenceBlock);
|
|
990
1074
|
const [pathAndIndex] = await committedDb.findSiblingPaths<MerkleTreeId.NOTE_HASH_TREE>(
|
|
991
1075
|
MerkleTreeId.NOTE_HASH_TREE,
|
|
992
1076
|
[noteHash],
|
|
@@ -1000,7 +1084,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1000
1084
|
referenceBlock: BlockParameter,
|
|
1001
1085
|
l1ToL2Message: Fr,
|
|
1002
1086
|
): Promise<[bigint, SiblingPath<typeof L1_TO_L2_MSG_TREE_HEIGHT>] | undefined> {
|
|
1003
|
-
const db = await this
|
|
1087
|
+
const db = await this.getWorldState(referenceBlock);
|
|
1004
1088
|
const [witness] = await db.findSiblingPaths(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, [l1ToL2Message]);
|
|
1005
1089
|
if (!witness) {
|
|
1006
1090
|
return undefined;
|
|
@@ -1010,11 +1094,9 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1010
1094
|
return [witness.index, witness.path];
|
|
1011
1095
|
}
|
|
1012
1096
|
|
|
1013
|
-
public async
|
|
1097
|
+
public async getL1ToL2MessageCheckpoint(l1ToL2Message: Fr): Promise<CheckpointNumber | undefined> {
|
|
1014
1098
|
const messageIndex = await this.l1ToL2MessageSource.getL1ToL2MessageIndex(l1ToL2Message);
|
|
1015
|
-
return messageIndex
|
|
1016
|
-
? BlockNumber.fromCheckpointNumber(InboxLeaf.checkpointNumberFromIndex(messageIndex))
|
|
1017
|
-
: undefined;
|
|
1099
|
+
return messageIndex ? InboxLeaf.checkpointNumberFromIndex(messageIndex) : undefined;
|
|
1018
1100
|
}
|
|
1019
1101
|
|
|
1020
1102
|
/**
|
|
@@ -1035,19 +1117,9 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1035
1117
|
public async getL2ToL1Messages(epoch: EpochNumber): Promise<Fr[][][][]> {
|
|
1036
1118
|
// Assumes `getCheckpointedBlocksForEpoch` returns blocks in ascending order of block number.
|
|
1037
1119
|
const checkpointedBlocks = await this.blockSource.getCheckpointedBlocksForEpoch(epoch);
|
|
1038
|
-
const blocksInCheckpoints
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
for (const checkpointedBlock of checkpointedBlocks) {
|
|
1042
|
-
const block = checkpointedBlock.block;
|
|
1043
|
-
const slotNumber = block.header.globalVariables.slotNumber;
|
|
1044
|
-
if (slotNumber !== previousSlotNumber) {
|
|
1045
|
-
checkpointIndex++;
|
|
1046
|
-
blocksInCheckpoints.push([]);
|
|
1047
|
-
previousSlotNumber = slotNumber;
|
|
1048
|
-
}
|
|
1049
|
-
blocksInCheckpoints[checkpointIndex].push(block);
|
|
1050
|
-
}
|
|
1120
|
+
const blocksInCheckpoints = chunkBy(checkpointedBlocks, cb => cb.block.header.globalVariables.slotNumber).map(
|
|
1121
|
+
group => group.map(cb => cb.block),
|
|
1122
|
+
);
|
|
1051
1123
|
return blocksInCheckpoints.map(blocks =>
|
|
1052
1124
|
blocks.map(block => block.body.txEffects.map(txEffect => txEffect.l2ToL1Msgs)),
|
|
1053
1125
|
);
|
|
@@ -1057,7 +1129,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1057
1129
|
referenceBlock: BlockParameter,
|
|
1058
1130
|
nullifier: Fr,
|
|
1059
1131
|
): Promise<NullifierMembershipWitness | undefined> {
|
|
1060
|
-
const db = await this
|
|
1132
|
+
const db = await this.getWorldState(referenceBlock);
|
|
1061
1133
|
const [witness] = await db.findSiblingPaths(MerkleTreeId.NULLIFIER_TREE, [nullifier.toBuffer()]);
|
|
1062
1134
|
if (!witness) {
|
|
1063
1135
|
return undefined;
|
|
@@ -1072,33 +1144,20 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1072
1144
|
return new NullifierMembershipWitness(index, leafPreimage as NullifierLeafPreimage, path);
|
|
1073
1145
|
}
|
|
1074
1146
|
|
|
1075
|
-
/**
|
|
1076
|
-
* Returns a low nullifier membership witness for a given nullifier at a given block.
|
|
1077
|
-
* @param referenceBlock - The block parameter (block number, block hash, or 'latest') at which to get the data
|
|
1078
|
-
* (which contains the root of the nullifier tree in which we are searching for the nullifier).
|
|
1079
|
-
* @param nullifier - Nullifier we try to find the low nullifier witness for.
|
|
1080
|
-
* @returns The low nullifier membership witness (if found).
|
|
1081
|
-
* @remarks Low nullifier witness can be used to perform a nullifier non-inclusion proof by leveraging the "linked
|
|
1082
|
-
* list structure" of leaves and proving that a lower nullifier is pointing to a bigger next value than the nullifier
|
|
1083
|
-
* we are trying to prove non-inclusion for.
|
|
1084
|
-
*
|
|
1085
|
-
* Note: This function returns the membership witness of the nullifier itself and not the low nullifier when
|
|
1086
|
-
* the nullifier already exists in the tree. This is because the `getPreviousValueIndex` function returns the
|
|
1087
|
-
* index of the nullifier itself when it already exists in the tree.
|
|
1088
|
-
* TODO: This is a confusing behavior and we should eventually address that.
|
|
1089
|
-
*/
|
|
1090
1147
|
public async getLowNullifierMembershipWitness(
|
|
1091
1148
|
referenceBlock: BlockParameter,
|
|
1092
1149
|
nullifier: Fr,
|
|
1093
1150
|
): Promise<NullifierMembershipWitness | undefined> {
|
|
1094
|
-
const committedDb = await this
|
|
1151
|
+
const committedDb = await this.getWorldState(referenceBlock);
|
|
1095
1152
|
const findResult = await committedDb.getPreviousValueIndex(MerkleTreeId.NULLIFIER_TREE, nullifier.toBigInt());
|
|
1096
1153
|
if (!findResult) {
|
|
1097
1154
|
return undefined;
|
|
1098
1155
|
}
|
|
1099
1156
|
const { index, alreadyPresent } = findResult;
|
|
1100
1157
|
if (alreadyPresent) {
|
|
1101
|
-
|
|
1158
|
+
throw new Error(
|
|
1159
|
+
`Cannot prove nullifier non-inclusion: nullifier ${nullifier.toBigInt()} already exists in the tree`,
|
|
1160
|
+
);
|
|
1102
1161
|
}
|
|
1103
1162
|
const preimageData = (await committedDb.getLeafPreimage(MerkleTreeId.NULLIFIER_TREE, index))!;
|
|
1104
1163
|
|
|
@@ -1107,7 +1166,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1107
1166
|
}
|
|
1108
1167
|
|
|
1109
1168
|
async getPublicDataWitness(referenceBlock: BlockParameter, leafSlot: Fr): Promise<PublicDataWitness | undefined> {
|
|
1110
|
-
const committedDb = await this
|
|
1169
|
+
const committedDb = await this.getWorldState(referenceBlock);
|
|
1111
1170
|
const lowLeafResult = await committedDb.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot.toBigInt());
|
|
1112
1171
|
if (!lowLeafResult) {
|
|
1113
1172
|
return undefined;
|
|
@@ -1122,7 +1181,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1122
1181
|
}
|
|
1123
1182
|
|
|
1124
1183
|
public async getPublicStorageAt(referenceBlock: BlockParameter, contract: AztecAddress, slot: Fr): Promise<Fr> {
|
|
1125
|
-
const committedDb = await this
|
|
1184
|
+
const committedDb = await this.getWorldState(referenceBlock);
|
|
1126
1185
|
const leafSlot = await computePublicDataTreeLeafSlot(contract, slot);
|
|
1127
1186
|
|
|
1128
1187
|
const lowLeafResult = await committedDb.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot.toBigInt());
|
|
@@ -1236,7 +1295,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1236
1295
|
const processor = publicProcessorFactory.create(merkleTreeFork, newGlobalVariables, config);
|
|
1237
1296
|
|
|
1238
1297
|
// REFACTOR: Consider merging ProcessReturnValues into ProcessedTx
|
|
1239
|
-
const [processedTxs, failedTxs, _usedTxs, returns] = await processor.process([tx]);
|
|
1298
|
+
const [processedTxs, failedTxs, _usedTxs, returns, debugLogs] = await processor.process([tx]);
|
|
1240
1299
|
// REFACTOR: Consider returning the error rather than throwing
|
|
1241
1300
|
if (failedTxs.length) {
|
|
1242
1301
|
this.log.warn(`Simulated tx ${txHash} fails: ${failedTxs[0].error}`, { txHash });
|
|
@@ -1250,6 +1309,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1250
1309
|
processedTx.txEffect,
|
|
1251
1310
|
returns,
|
|
1252
1311
|
processedTx.gasUsed,
|
|
1312
|
+
debugLogs,
|
|
1253
1313
|
);
|
|
1254
1314
|
} finally {
|
|
1255
1315
|
await merkleTreeFork.close();
|
|
@@ -1266,7 +1326,8 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1266
1326
|
// We accept transactions if they are not expired by the next slot (checked based on the ExpirationTimestamp field)
|
|
1267
1327
|
const { ts: nextSlotTimestamp } = this.epochCache.getEpochAndSlotInNextL1Slot();
|
|
1268
1328
|
const blockNumber = BlockNumber((await this.blockSource.getBlockNumber()) + 1);
|
|
1269
|
-
const
|
|
1329
|
+
const l1Constants = await this.blockSource.getL1Constants();
|
|
1330
|
+
const validator = createTxValidatorForAcceptingTxsOverRPC(
|
|
1270
1331
|
db,
|
|
1271
1332
|
this.contractDataSource,
|
|
1272
1333
|
verifier,
|
|
@@ -1275,10 +1336,16 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1275
1336
|
blockNumber,
|
|
1276
1337
|
l1ChainId: this.l1ChainId,
|
|
1277
1338
|
rollupVersion: this.version,
|
|
1278
|
-
setupAllowList:
|
|
1339
|
+
setupAllowList: [
|
|
1340
|
+
...(await getDefaultAllowedSetupFunctions()),
|
|
1341
|
+
...(this.config.txPublicSetupAllowListExtend ?? []),
|
|
1342
|
+
],
|
|
1279
1343
|
gasFees: await this.getCurrentMinFees(),
|
|
1280
1344
|
skipFeeEnforcement,
|
|
1281
1345
|
txsPermitted: !this.config.disableTransactions,
|
|
1346
|
+
rollupManaLimit: l1Constants.rollupManaLimit,
|
|
1347
|
+
maxBlockL2Gas: this.config.validateMaxL2BlockGas,
|
|
1348
|
+
maxBlockDAGas: this.config.validateMaxDABlockGas,
|
|
1282
1349
|
},
|
|
1283
1350
|
this.log.getBindings(),
|
|
1284
1351
|
);
|
|
@@ -1545,7 +1612,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1545
1612
|
* @param block - The block parameter (block number, block hash, or 'latest') at which to get the data.
|
|
1546
1613
|
* @returns An instance of a committed MerkleTreeOperations
|
|
1547
1614
|
*/
|
|
1548
|
-
async
|
|
1615
|
+
protected async getWorldState(block: BlockParameter) {
|
|
1549
1616
|
let blockSyncedTo: BlockNumber = BlockNumber.ZERO;
|
|
1550
1617
|
try {
|
|
1551
1618
|
// Attempt to sync the world state if necessary
|
|
@@ -1559,6 +1626,8 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1559
1626
|
return this.worldStateSynchronizer.getCommitted();
|
|
1560
1627
|
}
|
|
1561
1628
|
|
|
1629
|
+
// Get the block number, either directly from the parameter or by quering the archiver with the block hash
|
|
1630
|
+
let blockNumber: BlockNumber;
|
|
1562
1631
|
if (BlockHash.isBlockHash(block)) {
|
|
1563
1632
|
const initialBlockHash = await this.#getInitialHeaderHash();
|
|
1564
1633
|
if (block.equals(initialBlockHash)) {
|
|
@@ -1572,22 +1641,50 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
1572
1641
|
`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.`,
|
|
1573
1642
|
);
|
|
1574
1643
|
}
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1644
|
+
|
|
1645
|
+
blockNumber = header.getBlockNumber();
|
|
1646
|
+
} else {
|
|
1647
|
+
blockNumber = block as BlockNumber;
|
|
1648
|
+
}
|
|
1649
|
+
|
|
1650
|
+
// Check it's within world state sync range
|
|
1651
|
+
if (blockNumber > blockSyncedTo) {
|
|
1652
|
+
throw new Error(`Queried block ${block} not yet synced by the node (node is synced upto ${blockSyncedTo}).`);
|
|
1578
1653
|
}
|
|
1654
|
+
this.log.debug(`Using snapshot for block ${blockNumber}, world state synced upto ${blockSyncedTo}`);
|
|
1579
1655
|
|
|
1580
|
-
|
|
1581
|
-
{
|
|
1582
|
-
const blockNumber = block as BlockNumber;
|
|
1656
|
+
const snapshot = this.worldStateSynchronizer.getSnapshot(blockNumber);
|
|
1583
1657
|
|
|
1584
|
-
|
|
1585
|
-
|
|
1658
|
+
// Double-check world-state synced to the same block hash as was requested
|
|
1659
|
+
if (BlockHash.isBlockHash(block)) {
|
|
1660
|
+
const blockHash = await snapshot.getLeafValue(MerkleTreeId.ARCHIVE, BigInt(blockNumber));
|
|
1661
|
+
if (!blockHash || !new BlockHash(blockHash).equals(block)) {
|
|
1662
|
+
throw new Error(
|
|
1663
|
+
`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.`,
|
|
1664
|
+
);
|
|
1586
1665
|
}
|
|
1666
|
+
}
|
|
1667
|
+
|
|
1668
|
+
return snapshot;
|
|
1669
|
+
}
|
|
1587
1670
|
|
|
1588
|
-
|
|
1589
|
-
|
|
1671
|
+
/** Resolves a block parameter to a block number. */
|
|
1672
|
+
protected async resolveBlockNumber(block: BlockParameter): Promise<BlockNumber> {
|
|
1673
|
+
if (block === 'latest') {
|
|
1674
|
+
return BlockNumber(await this.blockSource.getBlockNumber());
|
|
1675
|
+
}
|
|
1676
|
+
if (BlockHash.isBlockHash(block)) {
|
|
1677
|
+
const initialBlockHash = await this.#getInitialHeaderHash();
|
|
1678
|
+
if (block.equals(initialBlockHash)) {
|
|
1679
|
+
return BlockNumber.ZERO;
|
|
1680
|
+
}
|
|
1681
|
+
const header = await this.blockSource.getBlockHeaderByHash(block);
|
|
1682
|
+
if (!header) {
|
|
1683
|
+
throw new Error(`Block hash ${block.toString()} not found.`);
|
|
1684
|
+
}
|
|
1685
|
+
return header.getBlockNumber();
|
|
1590
1686
|
}
|
|
1687
|
+
return block as BlockNumber;
|
|
1591
1688
|
}
|
|
1592
1689
|
|
|
1593
1690
|
/**
|