@aztec/validator-client 0.0.1-commit.e61ad554 → 0.0.1-commit.ec5f612
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 +21 -18
- package/dest/block_proposal_handler.d.ts +4 -5
- package/dest/block_proposal_handler.d.ts.map +1 -1
- package/dest/block_proposal_handler.js +23 -36
- package/dest/checkpoint_builder.d.ts +14 -12
- package/dest/checkpoint_builder.d.ts.map +1 -1
- package/dest/checkpoint_builder.js +51 -31
- package/dest/config.d.ts +1 -1
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +7 -4
- 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/index.d.ts +1 -2
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +0 -1
- package/dest/key_store/ha_key_store.d.ts +1 -1
- package/dest/key_store/ha_key_store.d.ts.map +1 -1
- package/dest/key_store/ha_key_store.js +2 -2
- package/dest/metrics.d.ts +4 -3
- package/dest/metrics.d.ts.map +1 -1
- package/dest/metrics.js +34 -5
- package/dest/validator.d.ts +38 -14
- package/dest/validator.d.ts.map +1 -1
- package/dest/validator.js +182 -51
- package/package.json +19 -17
- package/src/block_proposal_handler.ts +34 -53
- package/src/checkpoint_builder.ts +74 -30
- package/src/config.ts +7 -4
- package/src/duties/validation_service.ts +9 -2
- package/src/index.ts +0 -1
- package/src/key_store/ha_key_store.ts +2 -2
- package/src/metrics.ts +45 -6
- package/src/validator.ts +236 -63
- 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 -18
- 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 -135
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,33 +19,35 @@ 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 { P2P, PeerId
|
|
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,
|
|
30
|
+
ITxProvider,
|
|
29
31
|
Validator,
|
|
30
32
|
ValidatorClientFullConfig,
|
|
31
33
|
WorldStateSynchronizer,
|
|
32
34
|
} from '@aztec/stdlib/interfaces/server';
|
|
33
|
-
import type
|
|
34
|
-
import
|
|
35
|
-
BlockProposal,
|
|
36
|
-
BlockProposalOptions,
|
|
37
|
-
CheckpointAttestation,
|
|
38
|
-
|
|
39
|
-
|
|
35
|
+
import { type L1ToL2MessageSource, accumulateCheckpointOutHashes } from '@aztec/stdlib/messaging';
|
|
36
|
+
import {
|
|
37
|
+
type BlockProposal,
|
|
38
|
+
type BlockProposalOptions,
|
|
39
|
+
type CheckpointAttestation,
|
|
40
|
+
CheckpointProposal,
|
|
41
|
+
type CheckpointProposalCore,
|
|
42
|
+
type CheckpointProposalOptions,
|
|
40
43
|
} from '@aztec/stdlib/p2p';
|
|
41
|
-
import { CheckpointProposal } from '@aztec/stdlib/p2p';
|
|
42
44
|
import type { CheckpointHeader } from '@aztec/stdlib/rollup';
|
|
43
45
|
import type { BlockHeader, CheckpointGlobalVariables, Tx } from '@aztec/stdlib/tx';
|
|
44
46
|
import { AttestationTimeoutError } from '@aztec/stdlib/validators';
|
|
45
47
|
import { type TelemetryClient, type Tracer, getTelemetryClient } from '@aztec/telemetry-client';
|
|
46
48
|
import { createHASigner } from '@aztec/validator-ha-signer/factory';
|
|
47
49
|
import { DutyType, type SigningContext } from '@aztec/validator-ha-signer/types';
|
|
50
|
+
import type { ValidatorHASigner } from '@aztec/validator-ha-signer/validator-ha-signer';
|
|
48
51
|
|
|
49
52
|
import { EventEmitter } from 'events';
|
|
50
53
|
import type { TypedDataDefinition } from 'viem';
|
|
@@ -75,22 +78,22 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
75
78
|
private validationService: ValidationService;
|
|
76
79
|
private metrics: ValidatorMetrics;
|
|
77
80
|
private log: Logger;
|
|
78
|
-
|
|
79
81
|
// Whether it has already registered handlers on the p2p client
|
|
80
82
|
private hasRegisteredHandlers = false;
|
|
81
83
|
|
|
82
|
-
|
|
83
|
-
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;
|
|
84
89
|
|
|
85
90
|
private lastEpochForCommitteeUpdateLoop: EpochNumber | undefined;
|
|
86
91
|
private epochCacheUpdateLoop: RunningPromise;
|
|
87
92
|
|
|
88
93
|
private proposersOfInvalidBlocks: Set<string> = new Set();
|
|
89
94
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
// eslint-disable-next-line aztec-custom/no-non-primitive-in-collections
|
|
93
|
-
private validatedBlockSlots: Set<SlotNumber> = new Set();
|
|
95
|
+
/** Tracks the last checkpoint proposal we attested to, to prevent equivocation. */
|
|
96
|
+
private lastAttestedProposal?: CheckpointProposalCore;
|
|
94
97
|
|
|
95
98
|
protected constructor(
|
|
96
99
|
private keyStore: ExtendedValidatorKeyStore,
|
|
@@ -103,6 +106,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
103
106
|
private l1ToL2MessageSource: L1ToL2MessageSource,
|
|
104
107
|
private config: ValidatorClientFullConfig,
|
|
105
108
|
private blobClient: BlobClientInterface,
|
|
109
|
+
private haSigner: ValidatorHASigner | undefined,
|
|
106
110
|
private dateProvider: DateProvider = new DateProvider(),
|
|
107
111
|
telemetry: TelemetryClient = getTelemetryClient(),
|
|
108
112
|
log = createLogger('validator'),
|
|
@@ -184,7 +188,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
184
188
|
p2pClient: P2P,
|
|
185
189
|
blockSource: L2BlockSource & L2BlockSink,
|
|
186
190
|
l1ToL2MessageSource: L1ToL2MessageSource,
|
|
187
|
-
txProvider:
|
|
191
|
+
txProvider: ITxProvider,
|
|
188
192
|
keyStoreManager: KeystoreManager,
|
|
189
193
|
blobClient: BlobClientInterface,
|
|
190
194
|
dateProvider: DateProvider = new DateProvider(),
|
|
@@ -208,15 +212,18 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
208
212
|
telemetry,
|
|
209
213
|
);
|
|
210
214
|
|
|
211
|
-
|
|
215
|
+
const nodeKeystoreAdapter = NodeKeystoreAdapter.fromKeyStoreManager(keyStoreManager);
|
|
216
|
+
let validatorKeyStore: ExtendedValidatorKeyStore = nodeKeystoreAdapter;
|
|
217
|
+
let haSigner: ValidatorHASigner | undefined;
|
|
212
218
|
if (config.haSigningEnabled) {
|
|
213
219
|
// If maxStuckDutiesAgeMs is not explicitly set, compute it from Aztec slot duration
|
|
214
220
|
const haConfig = {
|
|
215
221
|
...config,
|
|
216
222
|
maxStuckDutiesAgeMs: config.maxStuckDutiesAgeMs ?? epochCache.getL1Constants().slotDuration * 2 * 1000,
|
|
217
223
|
};
|
|
218
|
-
const { signer } = await createHASigner(haConfig);
|
|
219
|
-
|
|
224
|
+
const { signer } = await createHASigner(haConfig, { telemetryClient: telemetry, dateProvider });
|
|
225
|
+
haSigner = signer;
|
|
226
|
+
validatorKeyStore = new HAKeyStore(nodeKeystoreAdapter, signer);
|
|
220
227
|
}
|
|
221
228
|
|
|
222
229
|
const validator = new ValidatorClient(
|
|
@@ -230,6 +237,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
230
237
|
l1ToL2MessageSource,
|
|
231
238
|
config,
|
|
232
239
|
blobClient,
|
|
240
|
+
haSigner,
|
|
233
241
|
dateProvider,
|
|
234
242
|
telemetry,
|
|
235
243
|
);
|
|
@@ -267,6 +275,28 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
267
275
|
this.config = { ...this.config, ...config };
|
|
268
276
|
}
|
|
269
277
|
|
|
278
|
+
public reloadKeystore(newManager: KeystoreManager): void {
|
|
279
|
+
if (this.config.haSigningEnabled && !this.haSigner) {
|
|
280
|
+
this.log.warn(
|
|
281
|
+
'HA signing is enabled in config but was not initialized at startup. ' +
|
|
282
|
+
'Restart the node to enable HA signing.',
|
|
283
|
+
);
|
|
284
|
+
} else if (!this.config.haSigningEnabled && this.haSigner) {
|
|
285
|
+
this.log.warn(
|
|
286
|
+
'HA signing was disabled via config update but the HA signer is still active. ' +
|
|
287
|
+
'Restart the node to fully disable HA signing.',
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const newAdapter = NodeKeystoreAdapter.fromKeyStoreManager(newManager);
|
|
292
|
+
if (this.haSigner) {
|
|
293
|
+
this.keyStore = new HAKeyStore(newAdapter, this.haSigner);
|
|
294
|
+
} else {
|
|
295
|
+
this.keyStore = newAdapter;
|
|
296
|
+
}
|
|
297
|
+
this.validationService = new ValidationService(this.keyStore, this.log.createChild('validation-service'));
|
|
298
|
+
}
|
|
299
|
+
|
|
270
300
|
public async start() {
|
|
271
301
|
if (this.epochCacheUpdateLoop.isRunning()) {
|
|
272
302
|
this.log.warn(`Validator client already started`);
|
|
@@ -313,6 +343,16 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
313
343
|
): Promise<CheckpointAttestation[] | undefined> => this.attestToCheckpointProposal(checkpoint, proposalSender);
|
|
314
344
|
this.p2pClient.registerCheckpointProposalHandler(checkpointHandler);
|
|
315
345
|
|
|
346
|
+
// Duplicate proposal handler - triggers slashing for equivocation
|
|
347
|
+
this.p2pClient.registerDuplicateProposalCallback((info: DuplicateProposalInfo) => {
|
|
348
|
+
this.handleDuplicateProposal(info);
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
// Duplicate attestation handler - triggers slashing for attestation equivocation
|
|
352
|
+
this.p2pClient.registerDuplicateAttestationCallback((info: DuplicateAttestationInfo) => {
|
|
353
|
+
this.handleDuplicateAttestation(info);
|
|
354
|
+
});
|
|
355
|
+
|
|
316
356
|
const myAddresses = this.getValidatorAddresses();
|
|
317
357
|
this.p2pClient.registerThisValidatorAddresses(myAddresses);
|
|
318
358
|
|
|
@@ -340,6 +380,15 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
340
380
|
return false;
|
|
341
381
|
}
|
|
342
382
|
|
|
383
|
+
// Ignore proposals from ourselves (may happen in HA setups)
|
|
384
|
+
if (this.getValidatorAddresses().some(addr => addr.equals(proposer))) {
|
|
385
|
+
this.log.warn(`Ignoring block proposal from self for slot ${slotNumber}`, {
|
|
386
|
+
proposer: proposer.toString(),
|
|
387
|
+
slotNumber,
|
|
388
|
+
});
|
|
389
|
+
return false;
|
|
390
|
+
}
|
|
391
|
+
|
|
343
392
|
// Check if we're in the committee (for metrics purposes)
|
|
344
393
|
const inCommittee = await this.epochCache.filterInCommittee(slotNumber, this.getValidatorAddresses());
|
|
345
394
|
const partOfCommittee = inCommittee.length > 0;
|
|
@@ -413,10 +462,6 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
413
462
|
return false;
|
|
414
463
|
}
|
|
415
464
|
|
|
416
|
-
// TODO(palla/mbps): Remove this once checkpoint validation is stable.
|
|
417
|
-
// Track that we successfully validated a block for this slot, so we can attest to checkpoint proposals for it.
|
|
418
|
-
this.validatedBlockSlots.add(slotNumber);
|
|
419
|
-
|
|
420
465
|
return true;
|
|
421
466
|
}
|
|
422
467
|
|
|
@@ -445,6 +490,23 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
445
490
|
return undefined;
|
|
446
491
|
}
|
|
447
492
|
|
|
493
|
+
// Ignore proposals from ourselves (may happen in HA setups)
|
|
494
|
+
if (this.getValidatorAddresses().some(addr => addr.equals(proposer))) {
|
|
495
|
+
this.log.warn(`Ignoring block proposal from self for slot ${slotNumber}`, {
|
|
496
|
+
proposer: proposer.toString(),
|
|
497
|
+
slotNumber,
|
|
498
|
+
});
|
|
499
|
+
return undefined;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// Validate fee asset price modifier is within allowed range
|
|
503
|
+
if (!validateFeeAssetPriceModifier(proposal.feeAssetPriceModifier)) {
|
|
504
|
+
this.log.warn(
|
|
505
|
+
`Received checkpoint proposal with invalid feeAssetPriceModifier ${proposal.feeAssetPriceModifier} for slot ${slotNumber}`,
|
|
506
|
+
);
|
|
507
|
+
return undefined;
|
|
508
|
+
}
|
|
509
|
+
|
|
448
510
|
// Check that I have any address in current committee before attesting
|
|
449
511
|
const inCommittee = await this.epochCache.filterInCommittee(slotNumber, this.getValidatorAddresses());
|
|
450
512
|
const partOfCommittee = inCommittee.length > 0;
|
|
@@ -461,17 +523,9 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
461
523
|
fishermanMode: this.config.fishermanMode || false,
|
|
462
524
|
});
|
|
463
525
|
|
|
464
|
-
// TODO(palla/mbps): Remove this once checkpoint validation is stable.
|
|
465
|
-
// Check that we have successfully validated a block for this slot before attesting to the checkpoint.
|
|
466
|
-
if (!this.validatedBlockSlots.has(slotNumber)) {
|
|
467
|
-
this.log.warn(`No validated block found for slot ${slotNumber}, refusing to attest to checkpoint`, proposalInfo);
|
|
468
|
-
return undefined;
|
|
469
|
-
}
|
|
470
|
-
|
|
471
526
|
// Validate the checkpoint proposal before attesting (unless skipCheckpointProposalValidation is set)
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
this.log.verbose(`Skipping checkpoint proposal validation for slot ${slotNumber}`, proposalInfo);
|
|
527
|
+
if (this.config.skipCheckpointProposalValidation) {
|
|
528
|
+
this.log.warn(`Skipping checkpoint proposal validation for slot ${slotNumber}`, proposalInfo);
|
|
475
529
|
} else {
|
|
476
530
|
const validationResult = await this.validateCheckpointProposal(proposal, proposalInfo);
|
|
477
531
|
if (!validationResult.isValid) {
|
|
@@ -526,15 +580,45 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
526
580
|
return undefined;
|
|
527
581
|
}
|
|
528
582
|
|
|
529
|
-
return this.createCheckpointAttestationsFromProposal(proposal, attestors);
|
|
583
|
+
return await this.createCheckpointAttestationsFromProposal(proposal, attestors);
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
/**
|
|
587
|
+
* Checks if we should attest to a slot based on equivocation prevention rules.
|
|
588
|
+
* @returns true if we should attest, false if we should skip
|
|
589
|
+
*/
|
|
590
|
+
private shouldAttestToSlot(slotNumber: SlotNumber): boolean {
|
|
591
|
+
// If attestToEquivocatedProposals is true, always allow
|
|
592
|
+
if (this.config.attestToEquivocatedProposals) {
|
|
593
|
+
return true;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
// Check if incoming slot is strictly greater than last attested
|
|
597
|
+
if (this.lastAttestedProposal && slotNumber <= this.lastAttestedProposal.slotNumber) {
|
|
598
|
+
this.log.warn(
|
|
599
|
+
`Refusing to process a proposal for slot ${slotNumber} given we already attested to a proposal for slot ${this.lastAttestedProposal.slotNumber}`,
|
|
600
|
+
);
|
|
601
|
+
return false;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
return true;
|
|
530
605
|
}
|
|
531
606
|
|
|
532
607
|
private async createCheckpointAttestationsFromProposal(
|
|
533
608
|
proposal: CheckpointProposalCore,
|
|
534
609
|
attestors: EthAddress[] = [],
|
|
535
|
-
): Promise<CheckpointAttestation[]> {
|
|
610
|
+
): Promise<CheckpointAttestation[] | undefined> {
|
|
611
|
+
// Equivocation check: must happen right before signing to minimize the race window
|
|
612
|
+
if (!this.shouldAttestToSlot(proposal.slotNumber)) {
|
|
613
|
+
return undefined;
|
|
614
|
+
}
|
|
615
|
+
|
|
536
616
|
const attestations = await this.validationService.attestToCheckpointProposal(proposal, attestors);
|
|
537
|
-
|
|
617
|
+
|
|
618
|
+
// Track the proposal we attested to (to prevent equivocation)
|
|
619
|
+
this.lastAttestedProposal = proposal;
|
|
620
|
+
|
|
621
|
+
await this.p2pClient.addOwnCheckpointAttestations(attestations);
|
|
538
622
|
return attestations;
|
|
539
623
|
}
|
|
540
624
|
|
|
@@ -547,7 +631,11 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
547
631
|
proposalInfo: LogData,
|
|
548
632
|
): Promise<{ isValid: true } | { isValid: false; reason: string }> {
|
|
549
633
|
const slot = proposal.slotNumber;
|
|
550
|
-
|
|
634
|
+
|
|
635
|
+
// Timeout block syncing at the start of the next slot
|
|
636
|
+
const config = this.checkpointsBuilder.getConfig();
|
|
637
|
+
const nextSlotTimestampSeconds = Number(getTimestampForSlot(SlotNumber(slot + 1), config));
|
|
638
|
+
const timeoutSeconds = Math.max(1, nextSlotTimestampSeconds - Math.floor(this.dateProvider.now() / 1000));
|
|
551
639
|
|
|
552
640
|
// Wait for last block to sync by archive
|
|
553
641
|
let lastBlockHeader: BlockHeader | undefined;
|
|
@@ -582,6 +670,12 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
582
670
|
return { isValid: false, reason: 'no_blocks_for_slot' };
|
|
583
671
|
}
|
|
584
672
|
|
|
673
|
+
// Ensure the last block for this slot matches the archive in the checkpoint proposal
|
|
674
|
+
if (!blocks.at(-1)?.archive.root.equals(proposal.archive)) {
|
|
675
|
+
this.log.warn(`Last block archive mismatch for checkpoint proposal`, proposalInfo);
|
|
676
|
+
return { isValid: false, reason: 'last_block_archive_mismatch' };
|
|
677
|
+
}
|
|
678
|
+
|
|
585
679
|
this.log.debug(`Found ${blocks.length} blocks for slot ${slot}`, {
|
|
586
680
|
...proposalInfo,
|
|
587
681
|
blockNumbers: blocks.map(b => b.number),
|
|
@@ -595,14 +689,11 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
595
689
|
// Get L1-to-L2 messages for this checkpoint
|
|
596
690
|
const l1ToL2Messages = await this.l1ToL2MessageSource.getL1ToL2Messages(checkpointNumber);
|
|
597
691
|
|
|
598
|
-
//
|
|
599
|
-
// TODO: There can be a more efficient way to get the previous checkpoint out hashes without having to fetch the
|
|
600
|
-
// actual checkpoints and the blocks/txs in them.
|
|
692
|
+
// Collect the out hashes of all the checkpoints before this one in the same epoch
|
|
601
693
|
const epoch = getEpochAtSlot(slot, this.epochCache.getL1Constants());
|
|
602
|
-
const
|
|
603
|
-
.filter(
|
|
604
|
-
.
|
|
605
|
-
const previousCheckpointOutHashes = previousCheckpoints.map(c => c.getCheckpointOutHash());
|
|
694
|
+
const previousCheckpointOutHashes = (await this.blockSource.getCheckpointsDataForEpoch(epoch))
|
|
695
|
+
.filter(c => c.checkpointNumber < checkpointNumber)
|
|
696
|
+
.map(c => c.checkpointOutHash);
|
|
606
697
|
|
|
607
698
|
// Fork world state at the block before the first block
|
|
608
699
|
const parentBlockNumber = BlockNumber(firstBlock.number - 1);
|
|
@@ -613,10 +704,12 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
613
704
|
const checkpointBuilder = await this.checkpointsBuilder.openCheckpoint(
|
|
614
705
|
checkpointNumber,
|
|
615
706
|
constants,
|
|
707
|
+
proposal.feeAssetPriceModifier,
|
|
616
708
|
l1ToL2Messages,
|
|
617
709
|
previousCheckpointOutHashes,
|
|
618
710
|
fork,
|
|
619
711
|
blocks,
|
|
712
|
+
this.log.getBindings(),
|
|
620
713
|
);
|
|
621
714
|
|
|
622
715
|
// Complete the checkpoint to get computed values
|
|
@@ -642,13 +735,17 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
642
735
|
return { isValid: false, reason: 'archive_mismatch' };
|
|
643
736
|
}
|
|
644
737
|
|
|
645
|
-
// Check that the accumulated out hash matches the value in the proposal.
|
|
646
|
-
|
|
647
|
-
const
|
|
648
|
-
|
|
738
|
+
// Check that the accumulated epoch out hash matches the value in the proposal.
|
|
739
|
+
// The epoch out hash is the accumulated hash of all checkpoint out hashes in the epoch.
|
|
740
|
+
const checkpointOutHash = computedCheckpoint.getCheckpointOutHash();
|
|
741
|
+
const computedEpochOutHash = accumulateCheckpointOutHashes([...previousCheckpointOutHashes, checkpointOutHash]);
|
|
742
|
+
const proposalEpochOutHash = proposal.checkpointHeader.epochOutHash;
|
|
743
|
+
if (!computedEpochOutHash.equals(proposalEpochOutHash)) {
|
|
649
744
|
this.log.warn(`Epoch out hash mismatch`, {
|
|
650
|
-
|
|
651
|
-
|
|
745
|
+
proposalEpochOutHash: proposalEpochOutHash.toString(),
|
|
746
|
+
computedEpochOutHash: computedEpochOutHash.toString(),
|
|
747
|
+
checkpointOutHash: checkpointOutHash.toString(),
|
|
748
|
+
previousCheckpointOutHashes: previousCheckpointOutHashes.map(h => h.toString()),
|
|
652
749
|
...proposalInfo,
|
|
653
750
|
});
|
|
654
751
|
return { isValid: false, reason: 'out_hash_mismatch' };
|
|
@@ -670,6 +767,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
670
767
|
chainId: gv.chainId,
|
|
671
768
|
version: gv.version,
|
|
672
769
|
slotNumber: gv.slotNumber,
|
|
770
|
+
timestamp: gv.timestamp,
|
|
673
771
|
coinbase: gv.coinbase,
|
|
674
772
|
feeRecipient: gv.feeRecipient,
|
|
675
773
|
gasFees: gv.gasFees,
|
|
@@ -679,7 +777,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
679
777
|
/**
|
|
680
778
|
* Uploads blobs for a checkpoint to the filestore (fire and forget).
|
|
681
779
|
*/
|
|
682
|
-
|
|
780
|
+
protected async uploadBlobsForCheckpoint(proposal: CheckpointProposalCore, proposalInfo: LogData): Promise<void> {
|
|
683
781
|
try {
|
|
684
782
|
const lastBlockHeader = await this.blockSource.getBlockHeaderByArchive(proposal.archive);
|
|
685
783
|
if (!lastBlockHeader) {
|
|
@@ -694,7 +792,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
694
792
|
}
|
|
695
793
|
|
|
696
794
|
const blobFields = blocks.flatMap(b => b.toBlobFields());
|
|
697
|
-
const blobs: Blob[] = getBlobsPerL1Block(blobFields);
|
|
795
|
+
const blobs: Blob[] = await getBlobsPerL1Block(blobFields);
|
|
698
796
|
await this.blobClient.sendBlobsToFilestore(blobs);
|
|
699
797
|
this.log.debug(`Uploaded ${blobs.length} blobs to filestore for checkpoint at slot ${proposal.slotNumber}`, {
|
|
700
798
|
...proposalInfo,
|
|
@@ -732,6 +830,52 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
732
830
|
]);
|
|
733
831
|
}
|
|
734
832
|
|
|
833
|
+
/**
|
|
834
|
+
* Handle detection of a duplicate proposal (equivocation).
|
|
835
|
+
* Emits a slash event when a proposer sends multiple proposals for the same position.
|
|
836
|
+
*/
|
|
837
|
+
private handleDuplicateProposal(info: DuplicateProposalInfo): void {
|
|
838
|
+
const { slot, proposer, type } = info;
|
|
839
|
+
|
|
840
|
+
this.log.warn(`Triggering slash event for duplicate ${type} proposal from ${proposer.toString()} at slot ${slot}`, {
|
|
841
|
+
proposer: proposer.toString(),
|
|
842
|
+
slot,
|
|
843
|
+
type,
|
|
844
|
+
});
|
|
845
|
+
|
|
846
|
+
// Emit slash event
|
|
847
|
+
this.emit(WANT_TO_SLASH_EVENT, [
|
|
848
|
+
{
|
|
849
|
+
validator: proposer,
|
|
850
|
+
amount: this.config.slashDuplicateProposalPenalty,
|
|
851
|
+
offenseType: OffenseType.DUPLICATE_PROPOSAL,
|
|
852
|
+
epochOrSlot: BigInt(slot),
|
|
853
|
+
},
|
|
854
|
+
]);
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
/**
|
|
858
|
+
* Handle detection of a duplicate attestation (equivocation).
|
|
859
|
+
* Emits a slash event when an attester signs attestations for different proposals at the same slot.
|
|
860
|
+
*/
|
|
861
|
+
private handleDuplicateAttestation(info: DuplicateAttestationInfo): void {
|
|
862
|
+
const { slot, attester } = info;
|
|
863
|
+
|
|
864
|
+
this.log.warn(`Triggering slash event for duplicate attestation from ${attester.toString()} at slot ${slot}`, {
|
|
865
|
+
attester: attester.toString(),
|
|
866
|
+
slot,
|
|
867
|
+
});
|
|
868
|
+
|
|
869
|
+
this.emit(WANT_TO_SLASH_EVENT, [
|
|
870
|
+
{
|
|
871
|
+
validator: attester,
|
|
872
|
+
amount: this.config.slashDuplicateAttestationPenalty,
|
|
873
|
+
offenseType: OffenseType.DUPLICATE_ATTESTATION,
|
|
874
|
+
epochOrSlot: BigInt(slot),
|
|
875
|
+
},
|
|
876
|
+
]);
|
|
877
|
+
}
|
|
878
|
+
|
|
735
879
|
async createBlockProposal(
|
|
736
880
|
blockHeader: BlockHeader,
|
|
737
881
|
indexWithinCheckpoint: IndexWithinCheckpoint,
|
|
@@ -739,13 +883,21 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
739
883
|
archive: Fr,
|
|
740
884
|
txs: Tx[],
|
|
741
885
|
proposerAddress: EthAddress | undefined,
|
|
742
|
-
options: BlockProposalOptions,
|
|
886
|
+
options: BlockProposalOptions = {},
|
|
743
887
|
): Promise<BlockProposal> {
|
|
744
|
-
//
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
888
|
+
// Validate that we're not creating a proposal for an older or equal position
|
|
889
|
+
if (this.lastProposedBlock) {
|
|
890
|
+
const lastSlot = this.lastProposedBlock.slotNumber;
|
|
891
|
+
const lastIndex = this.lastProposedBlock.indexWithinCheckpoint;
|
|
892
|
+
const newSlot = blockHeader.globalVariables.slotNumber;
|
|
893
|
+
|
|
894
|
+
if (newSlot < lastSlot || (newSlot === lastSlot && indexWithinCheckpoint <= lastIndex)) {
|
|
895
|
+
throw new Error(
|
|
896
|
+
`Cannot create block proposal for slot ${newSlot} index ${indexWithinCheckpoint}: ` +
|
|
897
|
+
`already proposed block for slot ${lastSlot} index ${lastIndex}`,
|
|
898
|
+
);
|
|
899
|
+
}
|
|
900
|
+
}
|
|
749
901
|
|
|
750
902
|
this.log.info(
|
|
751
903
|
`Assembling block proposal for block ${blockHeader.globalVariables.blockNumber} slot ${blockHeader.globalVariables.slotNumber}`,
|
|
@@ -762,25 +914,42 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
762
914
|
broadcastInvalidBlockProposal: this.config.broadcastInvalidBlockProposal,
|
|
763
915
|
},
|
|
764
916
|
);
|
|
765
|
-
this.
|
|
917
|
+
this.lastProposedBlock = newProposal;
|
|
766
918
|
return newProposal;
|
|
767
919
|
}
|
|
768
920
|
|
|
769
921
|
async createCheckpointProposal(
|
|
770
922
|
checkpointHeader: CheckpointHeader,
|
|
771
923
|
archive: Fr,
|
|
924
|
+
feeAssetPriceModifier: bigint,
|
|
772
925
|
lastBlockInfo: CreateCheckpointProposalLastBlockData | undefined,
|
|
773
926
|
proposerAddress: EthAddress | undefined,
|
|
774
|
-
options: CheckpointProposalOptions,
|
|
927
|
+
options: CheckpointProposalOptions = {},
|
|
775
928
|
): Promise<CheckpointProposal> {
|
|
929
|
+
// Validate that we're not creating a proposal for an older or equal slot
|
|
930
|
+
if (this.lastProposedCheckpoint) {
|
|
931
|
+
const lastSlot = this.lastProposedCheckpoint.slotNumber;
|
|
932
|
+
const newSlot = checkpointHeader.slotNumber;
|
|
933
|
+
|
|
934
|
+
if (newSlot <= lastSlot) {
|
|
935
|
+
throw new Error(
|
|
936
|
+
`Cannot create checkpoint proposal for slot ${newSlot}: ` +
|
|
937
|
+
`already proposed checkpoint for slot ${lastSlot}`,
|
|
938
|
+
);
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
|
|
776
942
|
this.log.info(`Assembling checkpoint proposal for slot ${checkpointHeader.slotNumber}`);
|
|
777
|
-
|
|
943
|
+
const newProposal = await this.validationService.createCheckpointProposal(
|
|
778
944
|
checkpointHeader,
|
|
779
945
|
archive,
|
|
946
|
+
feeAssetPriceModifier,
|
|
780
947
|
lastBlockInfo,
|
|
781
948
|
proposerAddress,
|
|
782
949
|
options,
|
|
783
950
|
);
|
|
951
|
+
this.lastProposedCheckpoint = newProposal;
|
|
952
|
+
return newProposal;
|
|
784
953
|
}
|
|
785
954
|
|
|
786
955
|
async broadcastBlockProposal(proposal: BlockProposal): Promise<void> {
|
|
@@ -802,6 +971,10 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
802
971
|
this.log.debug(`Collecting ${inCommittee.length} self-attestations for slot ${slot}`, { inCommittee });
|
|
803
972
|
const attestations = await this.createCheckpointAttestationsFromProposal(proposal, inCommittee);
|
|
804
973
|
|
|
974
|
+
if (!attestations) {
|
|
975
|
+
return [];
|
|
976
|
+
}
|
|
977
|
+
|
|
805
978
|
// We broadcast our own attestations to our peers so, in case our block does not get mined on L1,
|
|
806
979
|
// other nodes can see that our validators did attest to this block proposal, and do not slash us
|
|
807
980
|
// 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,18 +0,0 @@
|
|
|
1
|
-
import { BlockNumber } from '@aztec/foundation/branded-types';
|
|
2
|
-
import type { ContractDataSource } from '@aztec/stdlib/contract';
|
|
3
|
-
import type { GasFees } from '@aztec/stdlib/gas';
|
|
4
|
-
import type { AllowedElement, ClientProtocolCircuitVerifier, MerkleTreeReadOperations, PublicProcessorValidator } from '@aztec/stdlib/interfaces/server';
|
|
5
|
-
import { GlobalVariables, type Tx, type TxValidator } from '@aztec/stdlib/tx';
|
|
6
|
-
import type { UInt64 } from '@aztec/stdlib/types';
|
|
7
|
-
export declare function createValidatorForAcceptingTxs(db: MerkleTreeReadOperations, contractDataSource: ContractDataSource, verifier: ClientProtocolCircuitVerifier | undefined, { l1ChainId, rollupVersion, setupAllowList, gasFees, skipFeeEnforcement, timestamp, blockNumber, txsPermitted }: {
|
|
8
|
-
l1ChainId: number;
|
|
9
|
-
rollupVersion: number;
|
|
10
|
-
setupAllowList: AllowedElement[];
|
|
11
|
-
gasFees: GasFees;
|
|
12
|
-
skipFeeEnforcement?: boolean;
|
|
13
|
-
timestamp: UInt64;
|
|
14
|
-
blockNumber: BlockNumber;
|
|
15
|
-
txsPermitted: boolean;
|
|
16
|
-
}): TxValidator<Tx>;
|
|
17
|
-
export declare function createValidatorForBlockBuilding(db: MerkleTreeReadOperations, contractDataSource: ContractDataSource, globalVariables: GlobalVariables, setupAllowList: AllowedElement[]): PublicProcessorValidator;
|
|
18
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHhfdmFsaWRhdG9yX2ZhY3RvcnkuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy90eF92YWxpZGF0b3IvdHhfdmFsaWRhdG9yX2ZhY3RvcnkudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLGlDQUFpQyxDQUFDO0FBa0I5RCxPQUFPLEtBQUssRUFBRSxrQkFBa0IsRUFBRSxNQUFNLHdCQUF3QixDQUFDO0FBQ2pFLE9BQU8sS0FBSyxFQUFFLE9BQU8sRUFBRSxNQUFNLG1CQUFtQixDQUFDO0FBQ2pELE9BQU8sS0FBSyxFQUNWLGNBQWMsRUFDZCw2QkFBNkIsRUFDN0Isd0JBQXdCLEVBQ3hCLHdCQUF3QixFQUN6QixNQUFNLGlDQUFpQyxDQUFDO0FBRXpDLE9BQU8sRUFBRSxlQUFlLEVBQUUsS0FBSyxFQUFFLEVBQUUsS0FBSyxXQUFXLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUM5RSxPQUFPLEtBQUssRUFBRSxNQUFNLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUlsRCx3QkFBZ0IsOEJBQThCLENBQzVDLEVBQUUsRUFBRSx3QkFBd0IsRUFDNUIsa0JBQWtCLEVBQUUsa0JBQWtCLEVBQ3RDLFFBQVEsRUFBRSw2QkFBNkIsR0FBRyxTQUFTLEVBQ25ELEVBQ0UsU0FBUyxFQUNULGFBQWEsRUFDYixjQUFjLEVBQ2QsT0FBTyxFQUNQLGtCQUFrQixFQUNsQixTQUFTLEVBQ1QsV0FBVyxFQUNYLFlBQVksRUFDYixFQUFFO0lBQ0QsU0FBUyxFQUFFLE1BQU0sQ0FBQztJQUNsQixhQUFhLEVBQUUsTUFBTSxDQUFDO0lBQ3RCLGNBQWMsRUFBRSxjQUFjLEVBQUUsQ0FBQztJQUNqQyxPQUFPLEVBQUUsT0FBTyxDQUFDO0lBQ2pCLGtCQUFrQixDQUFDLEVBQUUsT0FBTyxDQUFDO0lBQzdCLFNBQVMsRUFBRSxNQUFNLENBQUM7SUFDbEIsV0FBVyxFQUFFLFdBQVcsQ0FBQztJQUN6QixZQUFZLEVBQUUsT0FBTyxDQUFDO0NBQ3ZCLEdBQ0EsV0FBVyxDQUFDLEVBQUUsQ0FBQyxDQTZCakI7QUFFRCx3QkFBZ0IsK0JBQStCLENBQzdDLEVBQUUsRUFBRSx3QkFBd0IsRUFDNUIsa0JBQWtCLEVBQUUsa0JBQWtCLEVBQ3RDLGVBQWUsRUFBRSxlQUFlLEVBQ2hDLGNBQWMsRUFBRSxjQUFjLEVBQUUsR0FDL0Isd0JBQXdCLENBZ0IxQiJ9
|
|
@@ -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;AAkB9D,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,GACA,WAAW,CAAC,EAAE,CAAC,CA6BjB;AAED,wBAAgB,+BAA+B,CAC7C,EAAE,EAAE,wBAAwB,EAC5B,kBAAkB,EAAE,kBAAkB,EACtC,eAAe,EAAE,eAAe,EAChC,cAAc,EAAE,cAAc,EAAE,GAC/B,wBAAwB,CAgB1B"}
|