@aztec/aztec-node 0.0.1-commit.8c0b8ff → 0.0.1-commit.8cb2d04d8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dest/aztec-node/config.d.ts +2 -2
- package/dest/aztec-node/config.d.ts.map +1 -1
- package/dest/aztec-node/config.js +1 -1
- package/dest/aztec-node/server.d.ts +15 -14
- package/dest/aztec-node/server.d.ts.map +1 -1
- package/dest/aztec-node/server.js +104 -52
- package/dest/sentinel/factory.d.ts +2 -2
- package/dest/sentinel/factory.d.ts.map +1 -1
- package/dest/sentinel/sentinel.js +6 -6
- package/package.json +26 -27
- package/src/aztec-node/config.ts +1 -1
- package/src/aztec-node/server.ts +112 -65
- package/src/sentinel/factory.ts +1 -1
- package/src/sentinel/sentinel.ts +6 -6
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,6 +9,7 @@ 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';
|
|
@@ -31,7 +33,12 @@ import {
|
|
|
31
33
|
import { ProtocolContractAddress } from '@aztec/protocol-contracts';
|
|
32
34
|
import { type ProverNode, type ProverNodeDeps, createProverNode } from '@aztec/prover-node';
|
|
33
35
|
import { createKeyStoreForProver } from '@aztec/prover-node/config';
|
|
34
|
-
import {
|
|
36
|
+
import {
|
|
37
|
+
FeeProviderImpl,
|
|
38
|
+
GlobalVariableBuilder,
|
|
39
|
+
SequencerClient,
|
|
40
|
+
type SequencerPublisher,
|
|
41
|
+
} from '@aztec/sequencer-client';
|
|
35
42
|
import { PublicProcessorFactory } from '@aztec/simulator/server';
|
|
36
43
|
import {
|
|
37
44
|
AttestationsBlockWatcher,
|
|
@@ -58,7 +65,7 @@ import type {
|
|
|
58
65
|
NodeInfo,
|
|
59
66
|
ProtocolContractAddresses,
|
|
60
67
|
} from '@aztec/stdlib/contract';
|
|
61
|
-
import { GasFees } from '@aztec/stdlib/gas';
|
|
68
|
+
import { GasFees, type ManaUsageEstimate } from '@aztec/stdlib/gas';
|
|
62
69
|
import { computePublicDataTreeLeafSlot } from '@aztec/stdlib/hash';
|
|
63
70
|
import {
|
|
64
71
|
type AztecNode,
|
|
@@ -81,11 +88,12 @@ import {
|
|
|
81
88
|
import type { DebugLogStore, LogFilter, SiloedTag, Tag, TxScopedL2Log } from '@aztec/stdlib/logs';
|
|
82
89
|
import { InMemoryDebugLogStore, NullDebugLogStore } from '@aztec/stdlib/logs';
|
|
83
90
|
import { InboxLeaf, type L1ToL2MessageSource } from '@aztec/stdlib/messaging';
|
|
84
|
-
import type { Offense
|
|
85
|
-
import type { NullifierLeafPreimage,
|
|
91
|
+
import type { Offense } from '@aztec/stdlib/slashing';
|
|
92
|
+
import type { NullifierLeafPreimage, PublicDataTreeLeafPreimage } from '@aztec/stdlib/trees';
|
|
86
93
|
import { MerkleTreeId, NullifierMembershipWitness, PublicDataWitness } from '@aztec/stdlib/trees';
|
|
87
94
|
import {
|
|
88
95
|
type BlockHeader,
|
|
96
|
+
type FeeProvider,
|
|
89
97
|
type GlobalVariableBuilder as GlobalVariableBuilderInterface,
|
|
90
98
|
type IndexedTxEffect,
|
|
91
99
|
PublicSimulationOutput,
|
|
@@ -97,6 +105,7 @@ import {
|
|
|
97
105
|
} from '@aztec/stdlib/tx';
|
|
98
106
|
import { getPackageVersion } from '@aztec/stdlib/update-checker';
|
|
99
107
|
import type { SingleValidatorStats, ValidatorsStats } from '@aztec/stdlib/validators';
|
|
108
|
+
import type { GenesisData } from '@aztec/stdlib/world-state';
|
|
100
109
|
import {
|
|
101
110
|
Attributes,
|
|
102
111
|
type TelemetryClient,
|
|
@@ -151,9 +160,11 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
151
160
|
protected readonly l1ChainId: number,
|
|
152
161
|
protected readonly version: number,
|
|
153
162
|
protected readonly globalVariableBuilder: GlobalVariableBuilderInterface,
|
|
163
|
+
protected readonly feeProvider: FeeProvider,
|
|
154
164
|
protected readonly epochCache: EpochCacheInterface,
|
|
155
165
|
protected readonly packageVersion: string,
|
|
156
|
-
private
|
|
166
|
+
private peerProofVerifier: ClientProtocolCircuitVerifier,
|
|
167
|
+
private rpcProofVerifier: ClientProtocolCircuitVerifier,
|
|
157
168
|
private telemetry: TelemetryClient = getTelemetryClient(),
|
|
158
169
|
private log = createLogger('node'),
|
|
159
170
|
private blobClient?: BlobClientInterface,
|
|
@@ -175,6 +186,11 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
175
186
|
}
|
|
176
187
|
}
|
|
177
188
|
|
|
189
|
+
/** @internal Exposed for testing — returns the RPC proof verifier. */
|
|
190
|
+
public getProofVerifier(): ClientProtocolCircuitVerifier {
|
|
191
|
+
return this.rpcProofVerifier;
|
|
192
|
+
}
|
|
193
|
+
|
|
178
194
|
public async getWorldStateSyncStatus(): Promise<WorldStateSyncStatus> {
|
|
179
195
|
const status = await this.worldStateSynchronizer.status();
|
|
180
196
|
return status.syncSummary;
|
|
@@ -201,7 +217,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
201
217
|
slashingProtectionDb?: SlashingProtectionDatabase;
|
|
202
218
|
} = {},
|
|
203
219
|
options: {
|
|
204
|
-
|
|
220
|
+
genesis?: GenesisData;
|
|
205
221
|
dontStartSequencer?: boolean;
|
|
206
222
|
dontStartProverNode?: boolean;
|
|
207
223
|
} = {},
|
|
@@ -304,16 +320,18 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
304
320
|
);
|
|
305
321
|
|
|
306
322
|
// now create the merkle trees and the world state synchronizer
|
|
307
|
-
const worldStateSynchronizer = await createWorldStateSynchronizer(
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
323
|
+
const worldStateSynchronizer = await createWorldStateSynchronizer(config, archiver, options.genesis, telemetry);
|
|
324
|
+
const useRealVerifiers = config.realProofs || config.debugForceTxProofVerification;
|
|
325
|
+
let peerProofVerifier: ClientProtocolCircuitVerifier;
|
|
326
|
+
let rpcProofVerifier: ClientProtocolCircuitVerifier;
|
|
327
|
+
if (useRealVerifiers) {
|
|
328
|
+
peerProofVerifier = await BatchChonkVerifier.new(config, config.bbChonkVerifyMaxBatch, 'peer');
|
|
329
|
+
const rpcVerifier = await BBCircuitVerifier.new(config);
|
|
330
|
+
rpcProofVerifier = new QueuedIVCVerifier(rpcVerifier, config.numConcurrentIVCVerifiers);
|
|
331
|
+
} else {
|
|
332
|
+
peerProofVerifier = new TestCircuitVerifier(config.proverTestVerificationDelayMs);
|
|
333
|
+
rpcProofVerifier = new TestCircuitVerifier(config.proverTestVerificationDelayMs);
|
|
334
|
+
}
|
|
317
335
|
|
|
318
336
|
let debugLogStore: DebugLogStore;
|
|
319
337
|
if (!config.realProofs) {
|
|
@@ -327,8 +345,6 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
327
345
|
debugLogStore = new NullDebugLogStore();
|
|
328
346
|
}
|
|
329
347
|
|
|
330
|
-
const proofVerifier = new QueuedIVCVerifier(config, circuitVerifier);
|
|
331
|
-
|
|
332
348
|
const proverOnly = config.enableProverNode && config.disableValidator;
|
|
333
349
|
if (proverOnly) {
|
|
334
350
|
log.info('Starting in prover-only mode: skipping validator, sequencer, sentinel, and slasher subsystems');
|
|
@@ -338,7 +354,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
338
354
|
const p2pClient = await createP2PClient(
|
|
339
355
|
config,
|
|
340
356
|
archiver,
|
|
341
|
-
|
|
357
|
+
peerProofVerifier,
|
|
342
358
|
worldStateSynchronizer,
|
|
343
359
|
epochCache,
|
|
344
360
|
packageVersion,
|
|
@@ -389,6 +405,11 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
389
405
|
// like attestations or auths will fail.
|
|
390
406
|
if (validatorClient) {
|
|
391
407
|
watchers.push(validatorClient);
|
|
408
|
+
|
|
409
|
+
const vc = validatorClient;
|
|
410
|
+
const getValidatorAddresses = () => vc.getValidatorAddresses().map(a => a.toString());
|
|
411
|
+
validatorClient.getProposalHandler().register(p2pClient, true, archiver, getValidatorAddresses);
|
|
412
|
+
|
|
392
413
|
if (!options.dontStartSequencer) {
|
|
393
414
|
await validatorClient.registerHandlers();
|
|
394
415
|
}
|
|
@@ -398,7 +419,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
398
419
|
// If there's no validator client, create a ProposalHandler to handle block and checkpoint proposals
|
|
399
420
|
// for monitoring or reexecution. Reexecution (default) allows us to follow the pending chain,
|
|
400
421
|
// while non-reexecution is used for validating the proposals and collecting their txs.
|
|
401
|
-
// Checkpoint proposals
|
|
422
|
+
// Checkpoint proposals rebuild blobs if the blob client can upload blobs.
|
|
402
423
|
if (!validatorClient) {
|
|
403
424
|
const reexecute = !!config.alwaysReexecuteBlockProposals;
|
|
404
425
|
log.info(`Setting up proposal handler` + (reexecute ? ' with reexecution of proposals' : ''));
|
|
@@ -412,7 +433,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
412
433
|
blobClient,
|
|
413
434
|
dateProvider,
|
|
414
435
|
telemetry,
|
|
415
|
-
}).register(p2pClient, reexecute);
|
|
436
|
+
}).register(p2pClient, reexecute, archiver);
|
|
416
437
|
}
|
|
417
438
|
|
|
418
439
|
// Start world state and wait for it to sync to the archiver.
|
|
@@ -454,7 +475,6 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
454
475
|
void archiver
|
|
455
476
|
.waitForInitialSync()
|
|
456
477
|
.then(async () => {
|
|
457
|
-
await p2pClient.start();
|
|
458
478
|
await validatorsSentinel?.start();
|
|
459
479
|
await epochPruneWatcher?.start();
|
|
460
480
|
await attestationsBlockWatcher?.start();
|
|
@@ -462,13 +482,16 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
462
482
|
})
|
|
463
483
|
.catch(err => log.error('Failed to start p2p services after archiver sync', err));
|
|
464
484
|
|
|
465
|
-
const
|
|
485
|
+
const globalVariableBuilderConfig = {
|
|
466
486
|
l1Contracts: config.l1Contracts,
|
|
467
487
|
ethereumSlotDuration: config.ethereumSlotDuration,
|
|
468
488
|
rollupVersion: BigInt(config.rollupVersion),
|
|
469
489
|
l1GenesisTime,
|
|
470
490
|
slotDuration: Number(slotDuration),
|
|
471
|
-
}
|
|
491
|
+
};
|
|
492
|
+
|
|
493
|
+
const globalVariableBuilder = new GlobalVariableBuilder(dateProvider, publicClient, globalVariableBuilderConfig);
|
|
494
|
+
const feeProvider = new FeeProviderImpl(dateProvider, publicClient, globalVariableBuilderConfig);
|
|
472
495
|
|
|
473
496
|
// Validator enabled, create/start relevant service
|
|
474
497
|
let sequencer: SequencerClient | undefined;
|
|
@@ -507,6 +530,19 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
507
530
|
{ telemetry, logger: log.createChild('l1-tx-utils'), dateProvider, kzg: Blob.getViemKzgInstance() },
|
|
508
531
|
);
|
|
509
532
|
|
|
533
|
+
// Create a funder L1TxUtils from the keystore funding account (if configured)
|
|
534
|
+
const fundingSigner = keyStoreManager?.createFundingSigner();
|
|
535
|
+
let funderL1TxUtils: L1TxUtils | undefined;
|
|
536
|
+
if (fundingSigner) {
|
|
537
|
+
const [funder] = await createL1TxUtilsFromSigners(
|
|
538
|
+
publicClient,
|
|
539
|
+
[fundingSigner],
|
|
540
|
+
{ ...config, scope: 'sequencer' },
|
|
541
|
+
{ telemetry, logger: log.createChild('l1-tx-utils:funder'), dateProvider },
|
|
542
|
+
);
|
|
543
|
+
funderL1TxUtils = funder;
|
|
544
|
+
}
|
|
545
|
+
|
|
510
546
|
// Create and start the sequencer client
|
|
511
547
|
const checkpointsBuilder = new CheckpointsBuilder(
|
|
512
548
|
{ ...config, l1GenesisTime, slotDuration: Number(slotDuration), rollupManaLimit },
|
|
@@ -521,6 +557,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
521
557
|
...deps,
|
|
522
558
|
epochCache,
|
|
523
559
|
l1TxUtils,
|
|
560
|
+
funderL1TxUtils,
|
|
524
561
|
validatorClient,
|
|
525
562
|
p2pClient,
|
|
526
563
|
worldStateSynchronizer,
|
|
@@ -582,9 +619,11 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
582
619
|
ethereumChain.chainInfo.id,
|
|
583
620
|
config.rollupVersion,
|
|
584
621
|
globalVariableBuilder,
|
|
622
|
+
feeProvider,
|
|
585
623
|
epochCache,
|
|
586
624
|
packageVersion,
|
|
587
|
-
|
|
625
|
+
peerProofVerifier,
|
|
626
|
+
rpcProofVerifier,
|
|
588
627
|
telemetry,
|
|
589
628
|
log,
|
|
590
629
|
blobClient,
|
|
@@ -733,12 +772,13 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
733
772
|
return this.blockSource.getCheckpointsDataForEpoch(epochNumber);
|
|
734
773
|
}
|
|
735
774
|
|
|
736
|
-
/**
|
|
737
|
-
* Method to fetch the current min L2 fees.
|
|
738
|
-
* @returns The current min L2 fees.
|
|
739
|
-
*/
|
|
740
775
|
public async getCurrentMinFees(): Promise<GasFees> {
|
|
741
|
-
return await this.
|
|
776
|
+
return await this.feeProvider.getCurrentMinFees();
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
/** Returns predicted min fees for the current slot and next N slots. */
|
|
780
|
+
public async getPredictedMinFees(manaUsage?: ManaUsageEstimate): Promise<GasFees[]> {
|
|
781
|
+
return await this.feeProvider.getPredictedMinFees(manaUsage);
|
|
742
782
|
}
|
|
743
783
|
|
|
744
784
|
public async getMaxPriorityFees(): Promise<GasFees> {
|
|
@@ -806,18 +846,22 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
806
846
|
page?: number,
|
|
807
847
|
referenceBlock?: BlockHash,
|
|
808
848
|
): Promise<TxScopedL2Log[][]> {
|
|
849
|
+
let upToBlockNumber: BlockNumber | undefined;
|
|
809
850
|
if (referenceBlock) {
|
|
810
851
|
const initialBlockHash = await this.#getInitialHeaderHash();
|
|
811
|
-
if (
|
|
852
|
+
if (referenceBlock.equals(initialBlockHash)) {
|
|
853
|
+
upToBlockNumber = BlockNumber(0);
|
|
854
|
+
} else {
|
|
812
855
|
const header = await this.blockSource.getBlockHeaderByHash(referenceBlock);
|
|
813
856
|
if (!header) {
|
|
814
857
|
throw new Error(
|
|
815
858
|
`Block ${referenceBlock.toString()} not found in the node. This might indicate a reorg has occurred.`,
|
|
816
859
|
);
|
|
817
860
|
}
|
|
861
|
+
upToBlockNumber = header.globalVariables.blockNumber;
|
|
818
862
|
}
|
|
819
863
|
}
|
|
820
|
-
return this.logsSource.getPrivateLogsByTags(tags, page);
|
|
864
|
+
return this.logsSource.getPrivateLogsByTags(tags, page, upToBlockNumber);
|
|
821
865
|
}
|
|
822
866
|
|
|
823
867
|
public async getPublicLogsByTagsFromContract(
|
|
@@ -826,18 +870,22 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
826
870
|
page?: number,
|
|
827
871
|
referenceBlock?: BlockHash,
|
|
828
872
|
): Promise<TxScopedL2Log[][]> {
|
|
873
|
+
let upToBlockNumber: BlockNumber | undefined;
|
|
829
874
|
if (referenceBlock) {
|
|
830
875
|
const initialBlockHash = await this.#getInitialHeaderHash();
|
|
831
|
-
if (
|
|
876
|
+
if (referenceBlock.equals(initialBlockHash)) {
|
|
877
|
+
upToBlockNumber = BlockNumber(0);
|
|
878
|
+
} else {
|
|
832
879
|
const header = await this.blockSource.getBlockHeaderByHash(referenceBlock);
|
|
833
880
|
if (!header) {
|
|
834
881
|
throw new Error(
|
|
835
882
|
`Block ${referenceBlock.toString()} not found in the node. This might indicate a reorg has occurred.`,
|
|
836
883
|
);
|
|
837
884
|
}
|
|
885
|
+
upToBlockNumber = header.globalVariables.blockNumber;
|
|
838
886
|
}
|
|
839
887
|
}
|
|
840
|
-
return this.logsSource.getPublicLogsByTagsFromContract(contractAddress, tags, page);
|
|
888
|
+
return this.logsSource.getPublicLogsByTagsFromContract(contractAddress, tags, page, upToBlockNumber);
|
|
841
889
|
}
|
|
842
890
|
|
|
843
891
|
/**
|
|
@@ -923,7 +971,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
923
971
|
await tryStop(this.validatorsSentinel);
|
|
924
972
|
await tryStop(this.epochPruneWatcher);
|
|
925
973
|
await tryStop(this.slasherClient);
|
|
926
|
-
await tryStop(this.
|
|
974
|
+
await Promise.all([tryStop(this.peerProofVerifier), tryStop(this.rpcProofVerifier)]);
|
|
927
975
|
await tryStop(this.sequencer);
|
|
928
976
|
await tryStop(this.proverNode);
|
|
929
977
|
await tryStop(this.p2pClient);
|
|
@@ -1013,7 +1061,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
1013
1061
|
);
|
|
1014
1062
|
|
|
1015
1063
|
// Build a map from block number to block hash
|
|
1016
|
-
const blockNumberToHash = new Map<BlockNumber,
|
|
1064
|
+
const blockNumberToHash = new Map<BlockNumber, BlockHash>();
|
|
1017
1065
|
for (let i = 0; i < uniqueBlockNumbers.length; i++) {
|
|
1018
1066
|
const blockHash = blockHashes[i];
|
|
1019
1067
|
if (blockHash === undefined) {
|
|
@@ -1031,13 +1079,13 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
1031
1079
|
if (blockNumber === undefined) {
|
|
1032
1080
|
throw new Error(`Block number not found for leaf index ${index} in tree ${MerkleTreeId[treeId]}`);
|
|
1033
1081
|
}
|
|
1034
|
-
const
|
|
1035
|
-
if (
|
|
1082
|
+
const l2BlockHash = blockNumberToHash.get(blockNumber);
|
|
1083
|
+
if (l2BlockHash === undefined) {
|
|
1036
1084
|
throw new Error(`Block hash not found for block number ${blockNumber}`);
|
|
1037
1085
|
}
|
|
1038
1086
|
return {
|
|
1039
1087
|
l2BlockNumber: blockNumber,
|
|
1040
|
-
l2BlockHash
|
|
1088
|
+
l2BlockHash,
|
|
1041
1089
|
data: index,
|
|
1042
1090
|
};
|
|
1043
1091
|
});
|
|
@@ -1047,20 +1095,14 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
1047
1095
|
referenceBlock: BlockParameter,
|
|
1048
1096
|
blockHash: BlockHash,
|
|
1049
1097
|
): Promise<MembershipWitness<typeof ARCHIVE_HEIGHT> | undefined> {
|
|
1050
|
-
// Block 0 (the initial block) has an empty archive, so no membership witness can exist.
|
|
1051
|
-
if (referenceBlock === BlockNumber.ZERO) {
|
|
1052
|
-
return undefined;
|
|
1053
|
-
}
|
|
1054
|
-
if (BlockHash.isBlockHash(referenceBlock)) {
|
|
1055
|
-
const initialBlockHash = await this.#getInitialHeaderHash();
|
|
1056
|
-
if (referenceBlock.equals(initialBlockHash)) {
|
|
1057
|
-
return undefined;
|
|
1058
|
-
}
|
|
1059
|
-
}
|
|
1060
1098
|
// The Noir circuit checks the archive membership proof against `anchor_block_header.last_archive.root`,
|
|
1061
1099
|
// which is the archive tree root BEFORE the anchor block was added (i.e. the state after block N-1).
|
|
1062
1100
|
// So we need the world state at block N-1, not block N, to produce a sibling path matching that root.
|
|
1063
1101
|
const referenceBlockNumber = await this.resolveBlockNumber(referenceBlock);
|
|
1102
|
+
if (referenceBlockNumber === BlockNumber.ZERO) {
|
|
1103
|
+
// Block 0 (the initial block) has an empty archive, so no membership witness can exist.
|
|
1104
|
+
return undefined;
|
|
1105
|
+
}
|
|
1064
1106
|
const committedDb = await this.getWorldState(BlockNumber(referenceBlockNumber - 1));
|
|
1065
1107
|
const [pathAndIndex] = await committedDb.findSiblingPaths<MerkleTreeId.ARCHIVE>(MerkleTreeId.ARCHIVE, [blockHash]);
|
|
1066
1108
|
return pathAndIndex === undefined
|
|
@@ -1323,7 +1365,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
1323
1365
|
{ isSimulation, skipFeeEnforcement }: { isSimulation?: boolean; skipFeeEnforcement?: boolean } = {},
|
|
1324
1366
|
): Promise<TxValidationResult> {
|
|
1325
1367
|
const db = this.worldStateSynchronizer.getCommitted();
|
|
1326
|
-
const verifier = isSimulation ? undefined : this.
|
|
1368
|
+
const verifier = isSimulation ? undefined : this.rpcProofVerifier;
|
|
1327
1369
|
|
|
1328
1370
|
// We accept transactions if they are not expired by the next slot (checked based on the ExpirationTimestamp field)
|
|
1329
1371
|
const { ts: nextSlotTimestamp } = this.epochCache.getEpochAndSlotInNextL1Slot();
|
|
@@ -1372,7 +1414,15 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
1372
1414
|
archiver.updateConfig(config);
|
|
1373
1415
|
}
|
|
1374
1416
|
if (newConfig.realProofs !== this.config.realProofs) {
|
|
1375
|
-
|
|
1417
|
+
await Promise.all([tryStop(this.peerProofVerifier), tryStop(this.rpcProofVerifier)]);
|
|
1418
|
+
if (newConfig.realProofs) {
|
|
1419
|
+
this.peerProofVerifier = await BatchChonkVerifier.new(newConfig, newConfig.bbChonkVerifyMaxBatch, 'peer');
|
|
1420
|
+
const rpcVerifier = await BBCircuitVerifier.new(newConfig);
|
|
1421
|
+
this.rpcProofVerifier = new QueuedIVCVerifier(rpcVerifier, newConfig.numConcurrentIVCVerifiers);
|
|
1422
|
+
} else {
|
|
1423
|
+
this.peerProofVerifier = new TestCircuitVerifier();
|
|
1424
|
+
this.rpcProofVerifier = new TestCircuitVerifier();
|
|
1425
|
+
}
|
|
1376
1426
|
}
|
|
1377
1427
|
|
|
1378
1428
|
this.config = newConfig;
|
|
@@ -1447,7 +1497,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
1447
1497
|
return Promise.resolve();
|
|
1448
1498
|
}
|
|
1449
1499
|
|
|
1450
|
-
public async rollbackTo(targetBlock: BlockNumber, force?: boolean): Promise<void> {
|
|
1500
|
+
public async rollbackTo(targetBlock: BlockNumber, force?: boolean, resumeSync = true): Promise<void> {
|
|
1451
1501
|
const archiver = this.blockSource as Archiver;
|
|
1452
1502
|
if (!('rollbackTo' in archiver)) {
|
|
1453
1503
|
throw new Error('Archiver implementation does not support rollbacks.');
|
|
@@ -1477,9 +1527,13 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
1477
1527
|
this.log.error(`Error during rollback`, err);
|
|
1478
1528
|
throw err;
|
|
1479
1529
|
} finally {
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1530
|
+
if (resumeSync) {
|
|
1531
|
+
this.log.info(`Resuming world state and archiver sync.`);
|
|
1532
|
+
this.worldStateSynchronizer.resumeSync();
|
|
1533
|
+
archiver.resume();
|
|
1534
|
+
} else {
|
|
1535
|
+
this.log.info(`Sync left paused after rollback (resumeSync=false).`);
|
|
1536
|
+
}
|
|
1483
1537
|
}
|
|
1484
1538
|
}
|
|
1485
1539
|
|
|
@@ -1496,19 +1550,12 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
1496
1550
|
return Promise.resolve();
|
|
1497
1551
|
}
|
|
1498
1552
|
|
|
1499
|
-
public getSlashPayloads(): Promise<SlashPayloadRound[]> {
|
|
1500
|
-
if (!this.slasherClient) {
|
|
1501
|
-
throw new Error(`Slasher client not enabled`);
|
|
1502
|
-
}
|
|
1503
|
-
return this.slasherClient.getSlashPayloads();
|
|
1504
|
-
}
|
|
1505
|
-
|
|
1506
1553
|
public getSlashOffenses(round: bigint | 'all' | 'current'): Promise<Offense[]> {
|
|
1507
1554
|
if (!this.slasherClient) {
|
|
1508
1555
|
throw new Error(`Slasher client not enabled`);
|
|
1509
1556
|
}
|
|
1510
1557
|
if (round === 'all') {
|
|
1511
|
-
return this.slasherClient.
|
|
1558
|
+
return this.slasherClient.getOffenses();
|
|
1512
1559
|
} else {
|
|
1513
1560
|
return this.slasherClient.gatherOffensesForRound(round === 'current' ? undefined : BigInt(round));
|
|
1514
1561
|
}
|
|
@@ -1694,7 +1741,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
|
|
|
1694
1741
|
// Double-check world-state synced to the same block hash as was requested
|
|
1695
1742
|
if (BlockHash.isBlockHash(block)) {
|
|
1696
1743
|
const blockHash = await snapshot.getLeafValue(MerkleTreeId.ARCHIVE, BigInt(blockNumber));
|
|
1697
|
-
if (!blockHash || !
|
|
1744
|
+
if (!blockHash || !block.equals(blockHash)) {
|
|
1698
1745
|
throw new Error(
|
|
1699
1746
|
`Block hash ${block.toString()} not found in world state at block number ${blockNumber}. If the node API has been queried with anchor block hash possibly a reorg has occurred.`,
|
|
1700
1747
|
);
|
package/src/sentinel/factory.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { EpochCache } from '@aztec/epoch-cache';
|
|
2
2
|
import { createLogger } from '@aztec/foundation/log';
|
|
3
|
-
import type { DataStoreConfig } from '@aztec/kv-store/config';
|
|
4
3
|
import { createStore } from '@aztec/kv-store/lmdb-v2';
|
|
5
4
|
import type { P2PClient } from '@aztec/p2p';
|
|
6
5
|
import type { L2BlockSource } from '@aztec/stdlib/block';
|
|
7
6
|
import type { SlasherConfig } from '@aztec/stdlib/interfaces/server';
|
|
7
|
+
import type { DataStoreConfig } from '@aztec/stdlib/kv-store';
|
|
8
8
|
|
|
9
9
|
import type { SentinelConfig } from './config.js';
|
|
10
10
|
import { Sentinel } from './sentinel.js';
|
package/src/sentinel/sentinel.ts
CHANGED
|
@@ -88,7 +88,7 @@ export class Sentinel extends (EventEmitter as new () => WatcherEmitter) impleme
|
|
|
88
88
|
|
|
89
89
|
/** Loads initial slot and initializes blockstream. We will not process anything at or before the initial slot. */
|
|
90
90
|
protected async init() {
|
|
91
|
-
this.initialSlot = this.epochCache.
|
|
91
|
+
this.initialSlot = this.epochCache.getSlotNow();
|
|
92
92
|
const startingBlock = BlockNumber(await this.archiver.getBlockNumber());
|
|
93
93
|
this.logger.info(`Starting validator sentinel with initial slot ${this.initialSlot} and block ${startingBlock}`);
|
|
94
94
|
this.blockStream = new L2BlockStream(this.archiver, this.l2TipsStore, this, this.logger, { startingBlock });
|
|
@@ -219,7 +219,7 @@ export class Sentinel extends (EventEmitter as new () => WatcherEmitter) impleme
|
|
|
219
219
|
// Check that we have at least requiredConsecutiveEpochs and that all of them are above the inactivity threshold
|
|
220
220
|
return pastEpochs
|
|
221
221
|
.slice(0, requiredConsecutiveEpochs)
|
|
222
|
-
.every(p => p.missed / p.total >= this.config.slashInactivityTargetPercentage);
|
|
222
|
+
.every(p => (p.total === 0 ? false : p.missed / p.total >= this.config.slashInactivityTargetPercentage));
|
|
223
223
|
}
|
|
224
224
|
|
|
225
225
|
protected async handleProvenPerformance(epoch: EpochNumber, performance: ValidatorsEpochPerformance) {
|
|
@@ -228,7 +228,7 @@ export class Sentinel extends (EventEmitter as new () => WatcherEmitter) impleme
|
|
|
228
228
|
}
|
|
229
229
|
|
|
230
230
|
const inactiveValidators = getEntries(performance)
|
|
231
|
-
.filter(([_, { missed, total }]) => missed / total >= this.config.slashInactivityTargetPercentage)
|
|
231
|
+
.filter(([_, { missed, total }]) => total > 0 && missed / total >= this.config.slashInactivityTargetPercentage)
|
|
232
232
|
.map(([address]) => address);
|
|
233
233
|
|
|
234
234
|
this.logger.debug(`Found ${inactiveValidators.length} inactive validators in epoch ${epoch}`, {
|
|
@@ -264,7 +264,7 @@ export class Sentinel extends (EventEmitter as new () => WatcherEmitter) impleme
|
|
|
264
264
|
* and we don't have that data if we were offline during the period.
|
|
265
265
|
*/
|
|
266
266
|
public async work() {
|
|
267
|
-
const
|
|
267
|
+
const currentSlot = this.epochCache.getSlotNow();
|
|
268
268
|
try {
|
|
269
269
|
// Manually sync the block stream to ensure we have the latest data.
|
|
270
270
|
// Note we never `start` the blockstream, so it loops at the same pace as we do.
|
|
@@ -436,7 +436,7 @@ export class Sentinel extends (EventEmitter as new () => WatcherEmitter) impleme
|
|
|
436
436
|
? fromEntries(await Promise.all(validators.map(async v => [v.toString(), await this.store.getHistory(v)])))
|
|
437
437
|
: await this.store.getHistories();
|
|
438
438
|
|
|
439
|
-
const slotNow = this.epochCache.
|
|
439
|
+
const slotNow = this.epochCache.getSlotNow();
|
|
440
440
|
fromSlot ??= SlotNumber(Math.max((this.lastProcessedSlot ?? slotNow) - this.store.getHistoryLength(), 0));
|
|
441
441
|
toSlot ??= this.lastProcessedSlot ?? slotNow;
|
|
442
442
|
|
|
@@ -464,7 +464,7 @@ export class Sentinel extends (EventEmitter as new () => WatcherEmitter) impleme
|
|
|
464
464
|
return undefined;
|
|
465
465
|
}
|
|
466
466
|
|
|
467
|
-
const slotNow = this.epochCache.
|
|
467
|
+
const slotNow = this.epochCache.getSlotNow();
|
|
468
468
|
const effectiveFromSlot =
|
|
469
469
|
fromSlot ?? SlotNumber(Math.max((this.lastProcessedSlot ?? slotNow) - this.store.getHistoryLength(), 0));
|
|
470
470
|
const effectiveToSlot = toSlot ?? this.lastProcessedSlot ?? slotNow;
|