@aztec/validator-client 0.0.1-commit.f2ce05ee → 0.0.1-commit.f504929
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/README.md +12 -10
- package/dest/block_proposal_handler.d.ts +2 -2
- package/dest/block_proposal_handler.d.ts.map +1 -1
- package/dest/block_proposal_handler.js +20 -34
- package/dest/checkpoint_builder.d.ts +8 -5
- package/dest/checkpoint_builder.d.ts.map +1 -1
- package/dest/checkpoint_builder.js +28 -18
- package/dest/config.d.ts +1 -1
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +5 -1
- package/dest/duties/validation_service.d.ts +2 -2
- package/dest/duties/validation_service.d.ts.map +1 -1
- package/dest/duties/validation_service.js +3 -3
- package/dest/factory.d.ts +1 -1
- package/dest/factory.d.ts.map +1 -1
- package/dest/factory.js +2 -1
- package/dest/index.d.ts +1 -2
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +0 -1
- package/dest/metrics.d.ts +9 -1
- package/dest/metrics.d.ts.map +1 -1
- package/dest/metrics.js +12 -0
- package/dest/validator.d.ts +30 -8
- package/dest/validator.d.ts.map +1 -1
- package/dest/validator.js +154 -28
- package/package.json +19 -19
- package/src/block_proposal_handler.ts +28 -48
- package/src/checkpoint_builder.ts +23 -6
- package/src/config.ts +5 -1
- package/src/duties/validation_service.ts +9 -2
- package/src/factory.ts +1 -0
- package/src/index.ts +0 -1
- package/src/metrics.ts +18 -0
- package/src/validator.ts +204 -34
- package/dest/tx_validator/index.d.ts +0 -3
- package/dest/tx_validator/index.d.ts.map +0 -1
- package/dest/tx_validator/index.js +0 -2
- package/dest/tx_validator/nullifier_cache.d.ts +0 -14
- package/dest/tx_validator/nullifier_cache.d.ts.map +0 -1
- package/dest/tx_validator/nullifier_cache.js +0 -24
- package/dest/tx_validator/tx_validator_factory.d.ts +0 -19
- package/dest/tx_validator/tx_validator_factory.d.ts.map +0 -1
- package/dest/tx_validator/tx_validator_factory.js +0 -54
- package/src/tx_validator/index.ts +0 -2
- package/src/tx_validator/nullifier_cache.ts +0 -30
- package/src/tx_validator/tx_validator_factory.ts +0 -154
package/src/validator.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { BlobClientInterface } from '@aztec/blob-client/client';
|
|
2
2
|
import { type Blob, getBlobsPerL1Block } from '@aztec/blob-lib';
|
|
3
3
|
import type { EpochCache } from '@aztec/epoch-cache';
|
|
4
|
+
import { validateFeeAssetPriceModifier } from '@aztec/ethereum/contracts';
|
|
4
5
|
import {
|
|
5
6
|
BlockNumber,
|
|
6
7
|
CheckpointNumber,
|
|
@@ -18,12 +19,12 @@ import { RunningPromise } from '@aztec/foundation/running-promise';
|
|
|
18
19
|
import { sleep } from '@aztec/foundation/sleep';
|
|
19
20
|
import { DateProvider } from '@aztec/foundation/timer';
|
|
20
21
|
import type { KeystoreManager } from '@aztec/node-keystore';
|
|
21
|
-
import type { DuplicateProposalInfo, P2P, PeerId } from '@aztec/p2p';
|
|
22
|
+
import type { DuplicateAttestationInfo, DuplicateProposalInfo, P2P, PeerId } from '@aztec/p2p';
|
|
22
23
|
import { AuthRequest, AuthResponse, BlockProposalValidator, ReqRespSubProtocol } from '@aztec/p2p';
|
|
23
24
|
import { OffenseType, WANT_TO_SLASH_EVENT, type Watcher, type WatcherEmitter } from '@aztec/slasher';
|
|
24
25
|
import type { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
25
26
|
import type { CommitteeAttestationsAndSigners, L2Block, L2BlockSink, L2BlockSource } from '@aztec/stdlib/block';
|
|
26
|
-
import { getEpochAtSlot } from '@aztec/stdlib/epoch-helpers';
|
|
27
|
+
import { getEpochAtSlot, getTimestampForSlot } from '@aztec/stdlib/epoch-helpers';
|
|
27
28
|
import type {
|
|
28
29
|
CreateCheckpointProposalLastBlockData,
|
|
29
30
|
ITxProvider,
|
|
@@ -32,20 +33,21 @@ import type {
|
|
|
32
33
|
WorldStateSynchronizer,
|
|
33
34
|
} from '@aztec/stdlib/interfaces/server';
|
|
34
35
|
import { type L1ToL2MessageSource, accumulateCheckpointOutHashes } from '@aztec/stdlib/messaging';
|
|
35
|
-
import
|
|
36
|
-
BlockProposal,
|
|
37
|
-
BlockProposalOptions,
|
|
38
|
-
CheckpointAttestation,
|
|
39
|
-
|
|
40
|
-
|
|
36
|
+
import {
|
|
37
|
+
type BlockProposal,
|
|
38
|
+
type BlockProposalOptions,
|
|
39
|
+
type CheckpointAttestation,
|
|
40
|
+
CheckpointProposal,
|
|
41
|
+
type CheckpointProposalCore,
|
|
42
|
+
type CheckpointProposalOptions,
|
|
41
43
|
} from '@aztec/stdlib/p2p';
|
|
42
|
-
import { CheckpointProposal } from '@aztec/stdlib/p2p';
|
|
43
44
|
import type { CheckpointHeader } from '@aztec/stdlib/rollup';
|
|
44
45
|
import type { BlockHeader, CheckpointGlobalVariables, Tx } from '@aztec/stdlib/tx';
|
|
45
46
|
import { AttestationTimeoutError } from '@aztec/stdlib/validators';
|
|
46
47
|
import { type TelemetryClient, type Tracer, getTelemetryClient } from '@aztec/telemetry-client';
|
|
47
48
|
import { createHASigner } from '@aztec/validator-ha-signer/factory';
|
|
48
49
|
import { DutyType, type SigningContext } from '@aztec/validator-ha-signer/types';
|
|
50
|
+
import type { ValidatorHASigner } from '@aztec/validator-ha-signer/validator-ha-signer';
|
|
49
51
|
|
|
50
52
|
import { EventEmitter } from 'events';
|
|
51
53
|
import type { TypedDataDefinition } from 'viem';
|
|
@@ -76,18 +78,25 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
76
78
|
private validationService: ValidationService;
|
|
77
79
|
private metrics: ValidatorMetrics;
|
|
78
80
|
private log: Logger;
|
|
79
|
-
|
|
80
81
|
// Whether it has already registered handlers on the p2p client
|
|
81
82
|
private hasRegisteredHandlers = false;
|
|
82
83
|
|
|
83
|
-
|
|
84
|
-
private
|
|
84
|
+
/** Tracks the last block proposal we created, to detect duplicate proposal attempts. */
|
|
85
|
+
private lastProposedBlock?: BlockProposal;
|
|
86
|
+
|
|
87
|
+
/** Tracks the last checkpoint proposal we created. */
|
|
88
|
+
private lastProposedCheckpoint?: CheckpointProposal;
|
|
85
89
|
|
|
86
90
|
private lastEpochForCommitteeUpdateLoop: EpochNumber | undefined;
|
|
87
91
|
private epochCacheUpdateLoop: RunningPromise;
|
|
92
|
+
/** Tracks the last epoch in which each attester successfully submitted at least one attestation. */
|
|
93
|
+
private lastAttestedEpochByAttester: Map<string, EpochNumber> = new Map();
|
|
88
94
|
|
|
89
95
|
private proposersOfInvalidBlocks: Set<string> = new Set();
|
|
90
96
|
|
|
97
|
+
/** Tracks the last checkpoint proposal we attested to, to prevent equivocation. */
|
|
98
|
+
private lastAttestedProposal?: CheckpointProposalCore;
|
|
99
|
+
|
|
91
100
|
protected constructor(
|
|
92
101
|
private keyStore: ExtendedValidatorKeyStore,
|
|
93
102
|
private epochCache: EpochCache,
|
|
@@ -99,6 +108,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
99
108
|
private l1ToL2MessageSource: L1ToL2MessageSource,
|
|
100
109
|
private config: ValidatorClientFullConfig,
|
|
101
110
|
private blobClient: BlobClientInterface,
|
|
111
|
+
private haSigner: ValidatorHASigner | undefined,
|
|
102
112
|
private dateProvider: DateProvider = new DateProvider(),
|
|
103
113
|
telemetry: TelemetryClient = getTelemetryClient(),
|
|
104
114
|
log = createLogger('validator'),
|
|
@@ -152,6 +162,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
152
162
|
this.log.trace(`No committee found for slot`);
|
|
153
163
|
return;
|
|
154
164
|
}
|
|
165
|
+
this.metrics.setCurrentEpoch(epoch);
|
|
155
166
|
if (epoch !== this.lastEpochForCommitteeUpdateLoop) {
|
|
156
167
|
const me = this.getValidatorAddresses();
|
|
157
168
|
const committeeSet = new Set(committee.map(v => v.toString()));
|
|
@@ -189,6 +200,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
189
200
|
const metrics = new ValidatorMetrics(telemetry);
|
|
190
201
|
const blockProposalValidator = new BlockProposalValidator(epochCache, {
|
|
191
202
|
txsPermitted: !config.disableTransactions,
|
|
203
|
+
maxTxsPerBlock: config.maxTxsPerBlock,
|
|
192
204
|
});
|
|
193
205
|
const blockProposalHandler = new BlockProposalHandler(
|
|
194
206
|
checkpointsBuilder,
|
|
@@ -204,15 +216,18 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
204
216
|
telemetry,
|
|
205
217
|
);
|
|
206
218
|
|
|
207
|
-
|
|
219
|
+
const nodeKeystoreAdapter = NodeKeystoreAdapter.fromKeyStoreManager(keyStoreManager);
|
|
220
|
+
let validatorKeyStore: ExtendedValidatorKeyStore = nodeKeystoreAdapter;
|
|
221
|
+
let haSigner: ValidatorHASigner | undefined;
|
|
208
222
|
if (config.haSigningEnabled) {
|
|
209
223
|
// If maxStuckDutiesAgeMs is not explicitly set, compute it from Aztec slot duration
|
|
210
224
|
const haConfig = {
|
|
211
225
|
...config,
|
|
212
226
|
maxStuckDutiesAgeMs: config.maxStuckDutiesAgeMs ?? epochCache.getL1Constants().slotDuration * 2 * 1000,
|
|
213
227
|
};
|
|
214
|
-
const { signer } = await createHASigner(haConfig);
|
|
215
|
-
|
|
228
|
+
const { signer } = await createHASigner(haConfig, { telemetryClient: telemetry, dateProvider });
|
|
229
|
+
haSigner = signer;
|
|
230
|
+
validatorKeyStore = new HAKeyStore(nodeKeystoreAdapter, signer);
|
|
216
231
|
}
|
|
217
232
|
|
|
218
233
|
const validator = new ValidatorClient(
|
|
@@ -226,6 +241,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
226
241
|
l1ToL2MessageSource,
|
|
227
242
|
config,
|
|
228
243
|
blobClient,
|
|
244
|
+
haSigner,
|
|
229
245
|
dateProvider,
|
|
230
246
|
telemetry,
|
|
231
247
|
);
|
|
@@ -263,6 +279,28 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
263
279
|
this.config = { ...this.config, ...config };
|
|
264
280
|
}
|
|
265
281
|
|
|
282
|
+
public reloadKeystore(newManager: KeystoreManager): void {
|
|
283
|
+
if (this.config.haSigningEnabled && !this.haSigner) {
|
|
284
|
+
this.log.warn(
|
|
285
|
+
'HA signing is enabled in config but was not initialized at startup. ' +
|
|
286
|
+
'Restart the node to enable HA signing.',
|
|
287
|
+
);
|
|
288
|
+
} else if (!this.config.haSigningEnabled && this.haSigner) {
|
|
289
|
+
this.log.warn(
|
|
290
|
+
'HA signing was disabled via config update but the HA signer is still active. ' +
|
|
291
|
+
'Restart the node to fully disable HA signing.',
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const newAdapter = NodeKeystoreAdapter.fromKeyStoreManager(newManager);
|
|
296
|
+
if (this.haSigner) {
|
|
297
|
+
this.keyStore = new HAKeyStore(newAdapter, this.haSigner);
|
|
298
|
+
} else {
|
|
299
|
+
this.keyStore = newAdapter;
|
|
300
|
+
}
|
|
301
|
+
this.validationService = new ValidationService(this.keyStore, this.log.createChild('validation-service'));
|
|
302
|
+
}
|
|
303
|
+
|
|
266
304
|
public async start() {
|
|
267
305
|
if (this.epochCacheUpdateLoop.isRunning()) {
|
|
268
306
|
this.log.warn(`Validator client already started`);
|
|
@@ -314,6 +352,11 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
314
352
|
this.handleDuplicateProposal(info);
|
|
315
353
|
});
|
|
316
354
|
|
|
355
|
+
// Duplicate attestation handler - triggers slashing for attestation equivocation
|
|
356
|
+
this.p2pClient.registerDuplicateAttestationCallback((info: DuplicateAttestationInfo) => {
|
|
357
|
+
this.handleDuplicateAttestation(info);
|
|
358
|
+
});
|
|
359
|
+
|
|
317
360
|
const myAddresses = this.getValidatorAddresses();
|
|
318
361
|
this.p2pClient.registerThisValidatorAddresses(myAddresses);
|
|
319
362
|
|
|
@@ -341,6 +384,15 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
341
384
|
return false;
|
|
342
385
|
}
|
|
343
386
|
|
|
387
|
+
// Ignore proposals from ourselves (may happen in HA setups)
|
|
388
|
+
if (this.getValidatorAddresses().some(addr => addr.equals(proposer))) {
|
|
389
|
+
this.log.warn(`Ignoring block proposal from self for slot ${slotNumber}`, {
|
|
390
|
+
proposer: proposer.toString(),
|
|
391
|
+
slotNumber,
|
|
392
|
+
});
|
|
393
|
+
return false;
|
|
394
|
+
}
|
|
395
|
+
|
|
344
396
|
// Check if we're in the committee (for metrics purposes)
|
|
345
397
|
const inCommittee = await this.epochCache.filterInCommittee(slotNumber, this.getValidatorAddresses());
|
|
346
398
|
const partOfCommittee = inCommittee.length > 0;
|
|
@@ -442,6 +494,23 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
442
494
|
return undefined;
|
|
443
495
|
}
|
|
444
496
|
|
|
497
|
+
// Ignore proposals from ourselves (may happen in HA setups)
|
|
498
|
+
if (this.getValidatorAddresses().some(addr => addr.equals(proposer))) {
|
|
499
|
+
this.log.warn(`Ignoring block proposal from self for slot ${slotNumber}`, {
|
|
500
|
+
proposer: proposer.toString(),
|
|
501
|
+
slotNumber,
|
|
502
|
+
});
|
|
503
|
+
return undefined;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
// Validate fee asset price modifier is within allowed range
|
|
507
|
+
if (!validateFeeAssetPriceModifier(proposal.feeAssetPriceModifier)) {
|
|
508
|
+
this.log.warn(
|
|
509
|
+
`Received checkpoint proposal with invalid feeAssetPriceModifier ${proposal.feeAssetPriceModifier} for slot ${slotNumber}`,
|
|
510
|
+
);
|
|
511
|
+
return undefined;
|
|
512
|
+
}
|
|
513
|
+
|
|
445
514
|
// Check that I have any address in current committee before attesting
|
|
446
515
|
const inCommittee = await this.epochCache.filterInCommittee(slotNumber, this.getValidatorAddresses());
|
|
447
516
|
const partOfCommittee = inCommittee.length > 0;
|
|
@@ -490,6 +559,17 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
490
559
|
|
|
491
560
|
this.metrics.incSuccessfulAttestations(inCommittee.length);
|
|
492
561
|
|
|
562
|
+
// Track epoch participation per attester: count each (attester, epoch) pair at most once
|
|
563
|
+
const proposalEpoch = getEpochAtSlot(slotNumber, this.epochCache.getL1Constants());
|
|
564
|
+
for (const attester of inCommittee) {
|
|
565
|
+
const key = attester.toString();
|
|
566
|
+
const lastEpoch = this.lastAttestedEpochByAttester.get(key);
|
|
567
|
+
if (lastEpoch === undefined || proposalEpoch > lastEpoch) {
|
|
568
|
+
this.lastAttestedEpochByAttester.set(key, proposalEpoch);
|
|
569
|
+
this.metrics.incAttestedEpochCount(attester);
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
|
|
493
573
|
// Determine which validators should attest
|
|
494
574
|
let attestors: EthAddress[];
|
|
495
575
|
if (partOfCommittee) {
|
|
@@ -515,14 +595,44 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
515
595
|
return undefined;
|
|
516
596
|
}
|
|
517
597
|
|
|
518
|
-
return this.createCheckpointAttestationsFromProposal(proposal, attestors);
|
|
598
|
+
return await this.createCheckpointAttestationsFromProposal(proposal, attestors);
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
/**
|
|
602
|
+
* Checks if we should attest to a slot based on equivocation prevention rules.
|
|
603
|
+
* @returns true if we should attest, false if we should skip
|
|
604
|
+
*/
|
|
605
|
+
private shouldAttestToSlot(slotNumber: SlotNumber): boolean {
|
|
606
|
+
// If attestToEquivocatedProposals is true, always allow
|
|
607
|
+
if (this.config.attestToEquivocatedProposals) {
|
|
608
|
+
return true;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
// Check if incoming slot is strictly greater than last attested
|
|
612
|
+
if (this.lastAttestedProposal && slotNumber <= this.lastAttestedProposal.slotNumber) {
|
|
613
|
+
this.log.warn(
|
|
614
|
+
`Refusing to process a proposal for slot ${slotNumber} given we already attested to a proposal for slot ${this.lastAttestedProposal.slotNumber}`,
|
|
615
|
+
);
|
|
616
|
+
return false;
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
return true;
|
|
519
620
|
}
|
|
520
621
|
|
|
521
622
|
private async createCheckpointAttestationsFromProposal(
|
|
522
623
|
proposal: CheckpointProposalCore,
|
|
523
624
|
attestors: EthAddress[] = [],
|
|
524
|
-
): Promise<CheckpointAttestation[]> {
|
|
625
|
+
): Promise<CheckpointAttestation[] | undefined> {
|
|
626
|
+
// Equivocation check: must happen right before signing to minimize the race window
|
|
627
|
+
if (!this.shouldAttestToSlot(proposal.slotNumber)) {
|
|
628
|
+
return undefined;
|
|
629
|
+
}
|
|
630
|
+
|
|
525
631
|
const attestations = await this.validationService.attestToCheckpointProposal(proposal, attestors);
|
|
632
|
+
|
|
633
|
+
// Track the proposal we attested to (to prevent equivocation)
|
|
634
|
+
this.lastAttestedProposal = proposal;
|
|
635
|
+
|
|
526
636
|
await this.p2pClient.addOwnCheckpointAttestations(attestations);
|
|
527
637
|
return attestations;
|
|
528
638
|
}
|
|
@@ -536,7 +646,11 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
536
646
|
proposalInfo: LogData,
|
|
537
647
|
): Promise<{ isValid: true } | { isValid: false; reason: string }> {
|
|
538
648
|
const slot = proposal.slotNumber;
|
|
539
|
-
|
|
649
|
+
|
|
650
|
+
// Timeout block syncing at the start of the next slot
|
|
651
|
+
const config = this.checkpointsBuilder.getConfig();
|
|
652
|
+
const nextSlotTimestampSeconds = Number(getTimestampForSlot(SlotNumber(slot + 1), config));
|
|
653
|
+
const timeoutSeconds = Math.max(1, nextSlotTimestampSeconds - Math.floor(this.dateProvider.now() / 1000));
|
|
540
654
|
|
|
541
655
|
// Wait for last block to sync by archive
|
|
542
656
|
let lastBlockHeader: BlockHeader | undefined;
|
|
@@ -571,6 +685,12 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
571
685
|
return { isValid: false, reason: 'no_blocks_for_slot' };
|
|
572
686
|
}
|
|
573
687
|
|
|
688
|
+
// Ensure the last block for this slot matches the archive in the checkpoint proposal
|
|
689
|
+
if (!blocks.at(-1)?.archive.root.equals(proposal.archive)) {
|
|
690
|
+
this.log.warn(`Last block archive mismatch for checkpoint proposal`, proposalInfo);
|
|
691
|
+
return { isValid: false, reason: 'last_block_archive_mismatch' };
|
|
692
|
+
}
|
|
693
|
+
|
|
574
694
|
this.log.debug(`Found ${blocks.length} blocks for slot ${slot}`, {
|
|
575
695
|
...proposalInfo,
|
|
576
696
|
blockNumbers: blocks.map(b => b.number),
|
|
@@ -584,14 +704,11 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
584
704
|
// Get L1-to-L2 messages for this checkpoint
|
|
585
705
|
const l1ToL2Messages = await this.l1ToL2MessageSource.getL1ToL2Messages(checkpointNumber);
|
|
586
706
|
|
|
587
|
-
//
|
|
588
|
-
// TODO: There can be a more efficient way to get the previous checkpoint out hashes without having to fetch the
|
|
589
|
-
// actual checkpoints and the blocks/txs in them.
|
|
707
|
+
// Collect the out hashes of all the checkpoints before this one in the same epoch
|
|
590
708
|
const epoch = getEpochAtSlot(slot, this.epochCache.getL1Constants());
|
|
591
|
-
const
|
|
592
|
-
.filter(
|
|
593
|
-
.
|
|
594
|
-
const previousCheckpointOutHashes = previousCheckpoints.map(c => c.getCheckpointOutHash());
|
|
709
|
+
const previousCheckpointOutHashes = (await this.blockSource.getCheckpointsDataForEpoch(epoch))
|
|
710
|
+
.filter(c => c.checkpointNumber < checkpointNumber)
|
|
711
|
+
.map(c => c.checkpointOutHash);
|
|
595
712
|
|
|
596
713
|
// Fork world state at the block before the first block
|
|
597
714
|
const parentBlockNumber = BlockNumber(firstBlock.number - 1);
|
|
@@ -602,6 +719,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
602
719
|
const checkpointBuilder = await this.checkpointsBuilder.openCheckpoint(
|
|
603
720
|
checkpointNumber,
|
|
604
721
|
constants,
|
|
722
|
+
proposal.feeAssetPriceModifier,
|
|
605
723
|
l1ToL2Messages,
|
|
606
724
|
previousCheckpointOutHashes,
|
|
607
725
|
fork,
|
|
@@ -664,6 +782,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
664
782
|
chainId: gv.chainId,
|
|
665
783
|
version: gv.version,
|
|
666
784
|
slotNumber: gv.slotNumber,
|
|
785
|
+
timestamp: gv.timestamp,
|
|
667
786
|
coinbase: gv.coinbase,
|
|
668
787
|
feeRecipient: gv.feeRecipient,
|
|
669
788
|
gasFees: gv.gasFees,
|
|
@@ -673,7 +792,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
673
792
|
/**
|
|
674
793
|
* Uploads blobs for a checkpoint to the filestore (fire and forget).
|
|
675
794
|
*/
|
|
676
|
-
|
|
795
|
+
protected async uploadBlobsForCheckpoint(proposal: CheckpointProposalCore, proposalInfo: LogData): Promise<void> {
|
|
677
796
|
try {
|
|
678
797
|
const lastBlockHeader = await this.blockSource.getBlockHeaderByArchive(proposal.archive);
|
|
679
798
|
if (!lastBlockHeader) {
|
|
@@ -688,7 +807,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
688
807
|
}
|
|
689
808
|
|
|
690
809
|
const blobFields = blocks.flatMap(b => b.toBlobFields());
|
|
691
|
-
const blobs: Blob[] = getBlobsPerL1Block(blobFields);
|
|
810
|
+
const blobs: Blob[] = await getBlobsPerL1Block(blobFields);
|
|
692
811
|
await this.blobClient.sendBlobsToFilestore(blobs);
|
|
693
812
|
this.log.debug(`Uploaded ${blobs.length} blobs to filestore for checkpoint at slot ${proposal.slotNumber}`, {
|
|
694
813
|
...proposalInfo,
|
|
@@ -750,6 +869,28 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
750
869
|
]);
|
|
751
870
|
}
|
|
752
871
|
|
|
872
|
+
/**
|
|
873
|
+
* Handle detection of a duplicate attestation (equivocation).
|
|
874
|
+
* Emits a slash event when an attester signs attestations for different proposals at the same slot.
|
|
875
|
+
*/
|
|
876
|
+
private handleDuplicateAttestation(info: DuplicateAttestationInfo): void {
|
|
877
|
+
const { slot, attester } = info;
|
|
878
|
+
|
|
879
|
+
this.log.warn(`Triggering slash event for duplicate attestation from ${attester.toString()} at slot ${slot}`, {
|
|
880
|
+
attester: attester.toString(),
|
|
881
|
+
slot,
|
|
882
|
+
});
|
|
883
|
+
|
|
884
|
+
this.emit(WANT_TO_SLASH_EVENT, [
|
|
885
|
+
{
|
|
886
|
+
validator: attester,
|
|
887
|
+
amount: this.config.slashDuplicateAttestationPenalty,
|
|
888
|
+
offenseType: OffenseType.DUPLICATE_ATTESTATION,
|
|
889
|
+
epochOrSlot: BigInt(slot),
|
|
890
|
+
},
|
|
891
|
+
]);
|
|
892
|
+
}
|
|
893
|
+
|
|
753
894
|
async createBlockProposal(
|
|
754
895
|
blockHeader: BlockHeader,
|
|
755
896
|
indexWithinCheckpoint: IndexWithinCheckpoint,
|
|
@@ -759,11 +900,19 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
759
900
|
proposerAddress: EthAddress | undefined,
|
|
760
901
|
options: BlockProposalOptions = {},
|
|
761
902
|
): Promise<BlockProposal> {
|
|
762
|
-
//
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
903
|
+
// Validate that we're not creating a proposal for an older or equal position
|
|
904
|
+
if (this.lastProposedBlock) {
|
|
905
|
+
const lastSlot = this.lastProposedBlock.slotNumber;
|
|
906
|
+
const lastIndex = this.lastProposedBlock.indexWithinCheckpoint;
|
|
907
|
+
const newSlot = blockHeader.globalVariables.slotNumber;
|
|
908
|
+
|
|
909
|
+
if (newSlot < lastSlot || (newSlot === lastSlot && indexWithinCheckpoint <= lastIndex)) {
|
|
910
|
+
throw new Error(
|
|
911
|
+
`Cannot create block proposal for slot ${newSlot} index ${indexWithinCheckpoint}: ` +
|
|
912
|
+
`already proposed block for slot ${lastSlot} index ${lastIndex}`,
|
|
913
|
+
);
|
|
914
|
+
}
|
|
915
|
+
}
|
|
767
916
|
|
|
768
917
|
this.log.info(
|
|
769
918
|
`Assembling block proposal for block ${blockHeader.globalVariables.blockNumber} slot ${blockHeader.globalVariables.slotNumber}`,
|
|
@@ -780,25 +929,42 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
780
929
|
broadcastInvalidBlockProposal: this.config.broadcastInvalidBlockProposal,
|
|
781
930
|
},
|
|
782
931
|
);
|
|
783
|
-
this.
|
|
932
|
+
this.lastProposedBlock = newProposal;
|
|
784
933
|
return newProposal;
|
|
785
934
|
}
|
|
786
935
|
|
|
787
936
|
async createCheckpointProposal(
|
|
788
937
|
checkpointHeader: CheckpointHeader,
|
|
789
938
|
archive: Fr,
|
|
939
|
+
feeAssetPriceModifier: bigint,
|
|
790
940
|
lastBlockInfo: CreateCheckpointProposalLastBlockData | undefined,
|
|
791
941
|
proposerAddress: EthAddress | undefined,
|
|
792
942
|
options: CheckpointProposalOptions = {},
|
|
793
943
|
): Promise<CheckpointProposal> {
|
|
944
|
+
// Validate that we're not creating a proposal for an older or equal slot
|
|
945
|
+
if (this.lastProposedCheckpoint) {
|
|
946
|
+
const lastSlot = this.lastProposedCheckpoint.slotNumber;
|
|
947
|
+
const newSlot = checkpointHeader.slotNumber;
|
|
948
|
+
|
|
949
|
+
if (newSlot <= lastSlot) {
|
|
950
|
+
throw new Error(
|
|
951
|
+
`Cannot create checkpoint proposal for slot ${newSlot}: ` +
|
|
952
|
+
`already proposed checkpoint for slot ${lastSlot}`,
|
|
953
|
+
);
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
|
|
794
957
|
this.log.info(`Assembling checkpoint proposal for slot ${checkpointHeader.slotNumber}`);
|
|
795
|
-
|
|
958
|
+
const newProposal = await this.validationService.createCheckpointProposal(
|
|
796
959
|
checkpointHeader,
|
|
797
960
|
archive,
|
|
961
|
+
feeAssetPriceModifier,
|
|
798
962
|
lastBlockInfo,
|
|
799
963
|
proposerAddress,
|
|
800
964
|
options,
|
|
801
965
|
);
|
|
966
|
+
this.lastProposedCheckpoint = newProposal;
|
|
967
|
+
return newProposal;
|
|
802
968
|
}
|
|
803
969
|
|
|
804
970
|
async broadcastBlockProposal(proposal: BlockProposal): Promise<void> {
|
|
@@ -820,6 +986,10 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
820
986
|
this.log.debug(`Collecting ${inCommittee.length} self-attestations for slot ${slot}`, { inCommittee });
|
|
821
987
|
const attestations = await this.createCheckpointAttestationsFromProposal(proposal, inCommittee);
|
|
822
988
|
|
|
989
|
+
if (!attestations) {
|
|
990
|
+
return [];
|
|
991
|
+
}
|
|
992
|
+
|
|
823
993
|
// We broadcast our own attestations to our peers so, in case our block does not get mined on L1,
|
|
824
994
|
// other nodes can see that our validators did attest to this block proposal, and do not slash us
|
|
825
995
|
// due to inactivity for missed attestations.
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
export * from './nullifier_cache.js';
|
|
2
|
-
export * from './tx_validator_factory.js';
|
|
3
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy90eF92YWxpZGF0b3IvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsY0FBYyxzQkFBc0IsQ0FBQztBQUNyQyxjQUFjLDJCQUEyQixDQUFDIn0=
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tx_validator/index.ts"],"names":[],"mappings":"AAAA,cAAc,sBAAsB,CAAC;AACrC,cAAc,2BAA2B,CAAC"}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import type { NullifierSource } from '@aztec/p2p';
|
|
2
|
-
import type { MerkleTreeReadOperations } from '@aztec/stdlib/interfaces/server';
|
|
3
|
-
/**
|
|
4
|
-
* Implements a nullifier source by checking a DB and an in-memory collection.
|
|
5
|
-
* Intended for validating transactions as they are added to a block.
|
|
6
|
-
*/
|
|
7
|
-
export declare class NullifierCache implements NullifierSource {
|
|
8
|
-
private db;
|
|
9
|
-
nullifiers: Set<string>;
|
|
10
|
-
constructor(db: MerkleTreeReadOperations);
|
|
11
|
-
nullifiersExist(nullifiers: Buffer[]): Promise<boolean[]>;
|
|
12
|
-
addNullifiers(nullifiers: Buffer[]): void;
|
|
13
|
-
}
|
|
14
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibnVsbGlmaWVyX2NhY2hlLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvdHhfdmFsaWRhdG9yL251bGxpZmllcl9jYWNoZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssRUFBRSxlQUFlLEVBQUUsTUFBTSxZQUFZLENBQUM7QUFDbEQsT0FBTyxLQUFLLEVBQUUsd0JBQXdCLEVBQUUsTUFBTSxpQ0FBaUMsQ0FBQztBQUdoRjs7O0dBR0c7QUFDSCxxQkFBYSxjQUFlLFlBQVcsZUFBZTtJQUd4QyxPQUFPLENBQUMsRUFBRTtJQUZ0QixVQUFVLEVBQUUsR0FBRyxDQUFDLE1BQU0sQ0FBQyxDQUFDO0lBRXhCLFlBQW9CLEVBQUUsRUFBRSx3QkFBd0IsRUFFL0M7SUFFWSxlQUFlLENBQUMsVUFBVSxFQUFFLE1BQU0sRUFBRSxHQUFHLE9BQU8sQ0FBQyxPQUFPLEVBQUUsQ0FBQyxDQU9yRTtJQUVNLGFBQWEsQ0FBQyxVQUFVLEVBQUUsTUFBTSxFQUFFLFFBSXhDO0NBQ0YifQ==
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"nullifier_cache.d.ts","sourceRoot":"","sources":["../../src/tx_validator/nullifier_cache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAClD,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,iCAAiC,CAAC;AAGhF;;;GAGG;AACH,qBAAa,cAAe,YAAW,eAAe;IAGxC,OAAO,CAAC,EAAE;IAFtB,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAExB,YAAoB,EAAE,EAAE,wBAAwB,EAE/C;IAEY,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAOrE;IAEM,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,QAIxC;CACF"}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { MerkleTreeId } from '@aztec/stdlib/trees';
|
|
2
|
-
/**
|
|
3
|
-
* Implements a nullifier source by checking a DB and an in-memory collection.
|
|
4
|
-
* Intended for validating transactions as they are added to a block.
|
|
5
|
-
*/ export class NullifierCache {
|
|
6
|
-
db;
|
|
7
|
-
nullifiers;
|
|
8
|
-
constructor(db){
|
|
9
|
-
this.db = db;
|
|
10
|
-
this.nullifiers = new Set();
|
|
11
|
-
}
|
|
12
|
-
async nullifiersExist(nullifiers) {
|
|
13
|
-
const cacheResults = nullifiers.map((n)=>this.nullifiers.has(n.toString()));
|
|
14
|
-
const toCheckDb = nullifiers.filter((_n, index)=>!cacheResults[index]);
|
|
15
|
-
const dbHits = await this.db.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, toCheckDb);
|
|
16
|
-
let dbIndex = 0;
|
|
17
|
-
return nullifiers.map((_n, index)=>cacheResults[index] || dbHits[dbIndex++] !== undefined);
|
|
18
|
-
}
|
|
19
|
-
addNullifiers(nullifiers) {
|
|
20
|
-
for (const nullifier of nullifiers){
|
|
21
|
-
this.nullifiers.add(nullifier.toString());
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { BlockNumber } from '@aztec/foundation/branded-types';
|
|
2
|
-
import type { LoggerBindings } from '@aztec/foundation/log';
|
|
3
|
-
import type { ContractDataSource } from '@aztec/stdlib/contract';
|
|
4
|
-
import type { GasFees } from '@aztec/stdlib/gas';
|
|
5
|
-
import type { AllowedElement, ClientProtocolCircuitVerifier, MerkleTreeReadOperations, PublicProcessorValidator } from '@aztec/stdlib/interfaces/server';
|
|
6
|
-
import { GlobalVariables, type Tx, type TxValidator } from '@aztec/stdlib/tx';
|
|
7
|
-
import type { UInt64 } from '@aztec/stdlib/types';
|
|
8
|
-
export declare function createValidatorForAcceptingTxs(db: MerkleTreeReadOperations, contractDataSource: ContractDataSource, verifier: ClientProtocolCircuitVerifier | undefined, { l1ChainId, rollupVersion, setupAllowList, gasFees, skipFeeEnforcement, timestamp, blockNumber, txsPermitted }: {
|
|
9
|
-
l1ChainId: number;
|
|
10
|
-
rollupVersion: number;
|
|
11
|
-
setupAllowList: AllowedElement[];
|
|
12
|
-
gasFees: GasFees;
|
|
13
|
-
skipFeeEnforcement?: boolean;
|
|
14
|
-
timestamp: UInt64;
|
|
15
|
-
blockNumber: BlockNumber;
|
|
16
|
-
txsPermitted: boolean;
|
|
17
|
-
}, bindings?: LoggerBindings): TxValidator<Tx>;
|
|
18
|
-
export declare function createValidatorForBlockBuilding(db: MerkleTreeReadOperations, contractDataSource: ContractDataSource, globalVariables: GlobalVariables, setupAllowList: AllowedElement[], bindings?: LoggerBindings): PublicProcessorValidator;
|
|
19
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHhfdmFsaWRhdG9yX2ZhY3RvcnkuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy90eF92YWxpZGF0b3IvdHhfdmFsaWRhdG9yX2ZhY3RvcnkudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLGlDQUFpQyxDQUFDO0FBRTlELE9BQU8sS0FBSyxFQUFFLGNBQWMsRUFBRSxNQUFNLHVCQUF1QixDQUFDO0FBaUI1RCxPQUFPLEtBQUssRUFBRSxrQkFBa0IsRUFBRSxNQUFNLHdCQUF3QixDQUFDO0FBQ2pFLE9BQU8sS0FBSyxFQUFFLE9BQU8sRUFBRSxNQUFNLG1CQUFtQixDQUFDO0FBQ2pELE9BQU8sS0FBSyxFQUNWLGNBQWMsRUFDZCw2QkFBNkIsRUFDN0Isd0JBQXdCLEVBQ3hCLHdCQUF3QixFQUN6QixNQUFNLGlDQUFpQyxDQUFDO0FBRXpDLE9BQU8sRUFBRSxlQUFlLEVBQUUsS0FBSyxFQUFFLEVBQUUsS0FBSyxXQUFXLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUM5RSxPQUFPLEtBQUssRUFBRSxNQUFNLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUlsRCx3QkFBZ0IsOEJBQThCLENBQzVDLEVBQUUsRUFBRSx3QkFBd0IsRUFDNUIsa0JBQWtCLEVBQUUsa0JBQWtCLEVBQ3RDLFFBQVEsRUFBRSw2QkFBNkIsR0FBRyxTQUFTLEVBQ25ELEVBQ0UsU0FBUyxFQUNULGFBQWEsRUFDYixjQUFjLEVBQ2QsT0FBTyxFQUNQLGtCQUFrQixFQUNsQixTQUFTLEVBQ1QsV0FBVyxFQUNYLFlBQVksRUFDYixFQUFFO0lBQ0QsU0FBUyxFQUFFLE1BQU0sQ0FBQztJQUNsQixhQUFhLEVBQUUsTUFBTSxDQUFDO0lBQ3RCLGNBQWMsRUFBRSxjQUFjLEVBQUUsQ0FBQztJQUNqQyxPQUFPLEVBQUUsT0FBTyxDQUFDO0lBQ2pCLGtCQUFrQixDQUFDLEVBQUUsT0FBTyxDQUFDO0lBQzdCLFNBQVMsRUFBRSxNQUFNLENBQUM7SUFDbEIsV0FBVyxFQUFFLFdBQVcsQ0FBQztJQUN6QixZQUFZLEVBQUUsT0FBTyxDQUFDO0NBQ3ZCLEVBQ0QsUUFBUSxDQUFDLEVBQUUsY0FBYyxHQUN4QixXQUFXLENBQUMsRUFBRSxDQUFDLENBcUNqQjtBQUVELHdCQUFnQiwrQkFBK0IsQ0FDN0MsRUFBRSxFQUFFLHdCQUF3QixFQUM1QixrQkFBa0IsRUFBRSxrQkFBa0IsRUFDdEMsZUFBZSxFQUFFLGVBQWUsRUFDaEMsY0FBYyxFQUFFLGNBQWMsRUFBRSxFQUNoQyxRQUFRLENBQUMsRUFBRSxjQUFjLEdBQ3hCLHdCQUF3QixDQWlCMUIifQ==
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"tx_validator_factory.d.ts","sourceRoot":"","sources":["../../src/tx_validator/tx_validator_factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAE9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAiB5D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,KAAK,EACV,cAAc,EACd,6BAA6B,EAC7B,wBAAwB,EACxB,wBAAwB,EACzB,MAAM,iCAAiC,CAAC;AAEzC,OAAO,EAAE,eAAe,EAAE,KAAK,EAAE,EAAE,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC9E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAIlD,wBAAgB,8BAA8B,CAC5C,EAAE,EAAE,wBAAwB,EAC5B,kBAAkB,EAAE,kBAAkB,EACtC,QAAQ,EAAE,6BAA6B,GAAG,SAAS,EACnD,EACE,SAAS,EACT,aAAa,EACb,cAAc,EACd,OAAO,EACP,kBAAkB,EAClB,SAAS,EACT,WAAW,EACX,YAAY,EACb,EAAE;IACD,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,cAAc,EAAE,CAAC;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,WAAW,CAAC;IACzB,YAAY,EAAE,OAAO,CAAC;CACvB,EACD,QAAQ,CAAC,EAAE,cAAc,GACxB,WAAW,CAAC,EAAE,CAAC,CAqCjB;AAED,wBAAgB,+BAA+B,CAC7C,EAAE,EAAE,wBAAwB,EAC5B,kBAAkB,EAAE,kBAAkB,EACtC,eAAe,EAAE,eAAe,EAChC,cAAc,EAAE,cAAc,EAAE,EAChC,QAAQ,CAAC,EAAE,cAAc,GACxB,wBAAwB,CAiB1B"}
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
2
|
-
import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types/vk-tree';
|
|
3
|
-
import { AggregateTxValidator, ArchiveCache, BlockHeaderTxValidator, DataTxValidator, DoubleSpendTxValidator, GasTxValidator, MetadataTxValidator, PhasesTxValidator, SizeTxValidator, TimestampTxValidator, TxPermittedValidator, TxProofValidator } from '@aztec/p2p';
|
|
4
|
-
import { ProtocolContractAddress, protocolContractsHash } from '@aztec/protocol-contracts';
|
|
5
|
-
import { DatabasePublicStateSource } from '@aztec/stdlib/trees';
|
|
6
|
-
import { NullifierCache } from './nullifier_cache.js';
|
|
7
|
-
export function createValidatorForAcceptingTxs(db, contractDataSource, verifier, { l1ChainId, rollupVersion, setupAllowList, gasFees, skipFeeEnforcement, timestamp, blockNumber, txsPermitted }, bindings) {
|
|
8
|
-
const validators = [
|
|
9
|
-
new TxPermittedValidator(txsPermitted, bindings),
|
|
10
|
-
new SizeTxValidator(bindings),
|
|
11
|
-
new DataTxValidator(bindings),
|
|
12
|
-
new MetadataTxValidator({
|
|
13
|
-
l1ChainId: new Fr(l1ChainId),
|
|
14
|
-
rollupVersion: new Fr(rollupVersion),
|
|
15
|
-
protocolContractsHash,
|
|
16
|
-
vkTreeRoot: getVKTreeRoot()
|
|
17
|
-
}, bindings),
|
|
18
|
-
new TimestampTxValidator({
|
|
19
|
-
timestamp,
|
|
20
|
-
blockNumber
|
|
21
|
-
}, bindings),
|
|
22
|
-
new DoubleSpendTxValidator(new NullifierCache(db), bindings),
|
|
23
|
-
new PhasesTxValidator(contractDataSource, setupAllowList, timestamp, bindings),
|
|
24
|
-
new BlockHeaderTxValidator(new ArchiveCache(db), bindings)
|
|
25
|
-
];
|
|
26
|
-
if (!skipFeeEnforcement) {
|
|
27
|
-
validators.push(new GasTxValidator(new DatabasePublicStateSource(db), ProtocolContractAddress.FeeJuice, gasFees, bindings));
|
|
28
|
-
}
|
|
29
|
-
if (verifier) {
|
|
30
|
-
validators.push(new TxProofValidator(verifier, bindings));
|
|
31
|
-
}
|
|
32
|
-
return new AggregateTxValidator(...validators);
|
|
33
|
-
}
|
|
34
|
-
export function createValidatorForBlockBuilding(db, contractDataSource, globalVariables, setupAllowList, bindings) {
|
|
35
|
-
const nullifierCache = new NullifierCache(db);
|
|
36
|
-
const archiveCache = new ArchiveCache(db);
|
|
37
|
-
const publicStateSource = new DatabasePublicStateSource(db);
|
|
38
|
-
return {
|
|
39
|
-
preprocessValidator: preprocessValidator(nullifierCache, archiveCache, publicStateSource, contractDataSource, globalVariables, setupAllowList, bindings),
|
|
40
|
-
nullifierCache
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
function preprocessValidator(nullifierCache, archiveCache, publicStateSource, contractDataSource, globalVariables, setupAllowList, bindings) {
|
|
44
|
-
// We don't include the TxProofValidator nor the DataTxValidator here because they are already checked by the time we get to block building.
|
|
45
|
-
return new AggregateTxValidator(new MetadataTxValidator({
|
|
46
|
-
l1ChainId: globalVariables.chainId,
|
|
47
|
-
rollupVersion: globalVariables.version,
|
|
48
|
-
protocolContractsHash,
|
|
49
|
-
vkTreeRoot: getVKTreeRoot()
|
|
50
|
-
}, bindings), new TimestampTxValidator({
|
|
51
|
-
timestamp: globalVariables.timestamp,
|
|
52
|
-
blockNumber: globalVariables.blockNumber
|
|
53
|
-
}, bindings), new DoubleSpendTxValidator(nullifierCache, bindings), new PhasesTxValidator(contractDataSource, setupAllowList, globalVariables.timestamp, bindings), new GasTxValidator(publicStateSource, ProtocolContractAddress.FeeJuice, globalVariables.gasFees, bindings), new BlockHeaderTxValidator(archiveCache, bindings));
|
|
54
|
-
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import type { NullifierSource } from '@aztec/p2p';
|
|
2
|
-
import type { MerkleTreeReadOperations } from '@aztec/stdlib/interfaces/server';
|
|
3
|
-
import { MerkleTreeId } from '@aztec/stdlib/trees';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Implements a nullifier source by checking a DB and an in-memory collection.
|
|
7
|
-
* Intended for validating transactions as they are added to a block.
|
|
8
|
-
*/
|
|
9
|
-
export class NullifierCache implements NullifierSource {
|
|
10
|
-
nullifiers: Set<string>;
|
|
11
|
-
|
|
12
|
-
constructor(private db: MerkleTreeReadOperations) {
|
|
13
|
-
this.nullifiers = new Set();
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
public async nullifiersExist(nullifiers: Buffer[]): Promise<boolean[]> {
|
|
17
|
-
const cacheResults = nullifiers.map(n => this.nullifiers.has(n.toString()));
|
|
18
|
-
const toCheckDb = nullifiers.filter((_n, index) => !cacheResults[index]);
|
|
19
|
-
const dbHits = await this.db.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, toCheckDb);
|
|
20
|
-
|
|
21
|
-
let dbIndex = 0;
|
|
22
|
-
return nullifiers.map((_n, index) => cacheResults[index] || dbHits[dbIndex++] !== undefined);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
public addNullifiers(nullifiers: Buffer[]) {
|
|
26
|
-
for (const nullifier of nullifiers) {
|
|
27
|
-
this.nullifiers.add(nullifier.toString());
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
}
|