@aztec/validator-client 0.0.1-commit.f1df4d2 → 0.0.1-commit.f224bb98b
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 +63 -18
- package/dest/block_proposal_handler.d.ts +5 -4
- package/dest/block_proposal_handler.d.ts.map +1 -1
- package/dest/block_proposal_handler.js +122 -62
- package/dest/checkpoint_builder.d.ts +15 -5
- package/dest/checkpoint_builder.d.ts.map +1 -1
- package/dest/checkpoint_builder.js +80 -25
- package/dest/config.d.ts +1 -1
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +26 -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 +5 -11
- 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/key_store/ha_key_store.js +1 -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 +35 -8
- package/dest/validator.d.ts.map +1 -1
- package/dest/validator.js +196 -34
- package/package.json +19 -19
- package/src/block_proposal_handler.ts +149 -80
- package/src/checkpoint_builder.ts +90 -14
- package/src/config.ts +26 -1
- package/src/duties/validation_service.ts +11 -10
- package/src/factory.ts +1 -0
- package/src/index.ts +0 -1
- package/src/key_store/ha_key_store.ts +1 -1
- package/src/metrics.ts +18 -0
- package/src/validator.ts +246 -40
- 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,13 @@ 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 } 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 {
|
|
27
|
+
import { validateCheckpoint } from '@aztec/stdlib/checkpoint';
|
|
28
|
+
import { getEpochAtSlot, getTimestampForSlot } from '@aztec/stdlib/epoch-helpers';
|
|
27
29
|
import type {
|
|
28
30
|
CreateCheckpointProposalLastBlockData,
|
|
29
31
|
ITxProvider,
|
|
@@ -32,20 +34,21 @@ import type {
|
|
|
32
34
|
WorldStateSynchronizer,
|
|
33
35
|
} from '@aztec/stdlib/interfaces/server';
|
|
34
36
|
import { type L1ToL2MessageSource, accumulateCheckpointOutHashes } from '@aztec/stdlib/messaging';
|
|
35
|
-
import
|
|
36
|
-
BlockProposal,
|
|
37
|
-
BlockProposalOptions,
|
|
38
|
-
CheckpointAttestation,
|
|
39
|
-
|
|
40
|
-
|
|
37
|
+
import {
|
|
38
|
+
type BlockProposal,
|
|
39
|
+
type BlockProposalOptions,
|
|
40
|
+
type CheckpointAttestation,
|
|
41
|
+
CheckpointProposal,
|
|
42
|
+
type CheckpointProposalCore,
|
|
43
|
+
type CheckpointProposalOptions,
|
|
41
44
|
} from '@aztec/stdlib/p2p';
|
|
42
|
-
import { CheckpointProposal } from '@aztec/stdlib/p2p';
|
|
43
45
|
import type { CheckpointHeader } from '@aztec/stdlib/rollup';
|
|
44
46
|
import type { BlockHeader, CheckpointGlobalVariables, Tx } from '@aztec/stdlib/tx';
|
|
45
47
|
import { AttestationTimeoutError } from '@aztec/stdlib/validators';
|
|
46
48
|
import { type TelemetryClient, type Tracer, getTelemetryClient } from '@aztec/telemetry-client';
|
|
47
|
-
import { createHASigner } from '@aztec/validator-ha-signer/factory';
|
|
49
|
+
import { createHASigner, createLocalSignerWithProtection } from '@aztec/validator-ha-signer/factory';
|
|
48
50
|
import { DutyType, type SigningContext } from '@aztec/validator-ha-signer/types';
|
|
51
|
+
import type { ValidatorHASigner } from '@aztec/validator-ha-signer/validator-ha-signer';
|
|
49
52
|
|
|
50
53
|
import { EventEmitter } from 'events';
|
|
51
54
|
import type { TypedDataDefinition } from 'viem';
|
|
@@ -76,18 +79,25 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
76
79
|
private validationService: ValidationService;
|
|
77
80
|
private metrics: ValidatorMetrics;
|
|
78
81
|
private log: Logger;
|
|
79
|
-
|
|
80
82
|
// Whether it has already registered handlers on the p2p client
|
|
81
83
|
private hasRegisteredHandlers = false;
|
|
82
84
|
|
|
83
|
-
|
|
84
|
-
private
|
|
85
|
+
/** Tracks the last block proposal we created, to detect duplicate proposal attempts. */
|
|
86
|
+
private lastProposedBlock?: BlockProposal;
|
|
87
|
+
|
|
88
|
+
/** Tracks the last checkpoint proposal we created. */
|
|
89
|
+
private lastProposedCheckpoint?: CheckpointProposal;
|
|
85
90
|
|
|
86
91
|
private lastEpochForCommitteeUpdateLoop: EpochNumber | undefined;
|
|
87
92
|
private epochCacheUpdateLoop: RunningPromise;
|
|
93
|
+
/** Tracks the last epoch in which each attester successfully submitted at least one attestation. */
|
|
94
|
+
private lastAttestedEpochByAttester: Map<string, EpochNumber> = new Map();
|
|
88
95
|
|
|
89
96
|
private proposersOfInvalidBlocks: Set<string> = new Set();
|
|
90
97
|
|
|
98
|
+
/** Tracks the last checkpoint proposal we attested to, to prevent equivocation. */
|
|
99
|
+
private lastAttestedProposal?: CheckpointProposalCore;
|
|
100
|
+
|
|
91
101
|
protected constructor(
|
|
92
102
|
private keyStore: ExtendedValidatorKeyStore,
|
|
93
103
|
private epochCache: EpochCache,
|
|
@@ -99,6 +109,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
99
109
|
private l1ToL2MessageSource: L1ToL2MessageSource,
|
|
100
110
|
private config: ValidatorClientFullConfig,
|
|
101
111
|
private blobClient: BlobClientInterface,
|
|
112
|
+
private slashingProtectionSigner: ValidatorHASigner,
|
|
102
113
|
private dateProvider: DateProvider = new DateProvider(),
|
|
103
114
|
telemetry: TelemetryClient = getTelemetryClient(),
|
|
104
115
|
log = createLogger('validator'),
|
|
@@ -152,6 +163,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
152
163
|
this.log.trace(`No committee found for slot`);
|
|
153
164
|
return;
|
|
154
165
|
}
|
|
166
|
+
this.metrics.setCurrentEpoch(epoch);
|
|
155
167
|
if (epoch !== this.lastEpochForCommitteeUpdateLoop) {
|
|
156
168
|
const me = this.getValidatorAddresses();
|
|
157
169
|
const committeeSet = new Set(committee.map(v => v.toString()));
|
|
@@ -189,6 +201,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
189
201
|
const metrics = new ValidatorMetrics(telemetry);
|
|
190
202
|
const blockProposalValidator = new BlockProposalValidator(epochCache, {
|
|
191
203
|
txsPermitted: !config.disableTransactions,
|
|
204
|
+
maxTxsPerBlock: config.validateMaxTxsPerBlock,
|
|
192
205
|
});
|
|
193
206
|
const blockProposalHandler = new BlockProposalHandler(
|
|
194
207
|
checkpointsBuilder,
|
|
@@ -204,16 +217,28 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
204
217
|
telemetry,
|
|
205
218
|
);
|
|
206
219
|
|
|
207
|
-
|
|
220
|
+
const nodeKeystoreAdapter = NodeKeystoreAdapter.fromKeyStoreManager(keyStoreManager);
|
|
221
|
+
let slashingProtectionSigner: ValidatorHASigner;
|
|
208
222
|
if (config.haSigningEnabled) {
|
|
223
|
+
// Multi-node HA mode: use PostgreSQL-backed distributed locking.
|
|
209
224
|
// If maxStuckDutiesAgeMs is not explicitly set, compute it from Aztec slot duration
|
|
210
225
|
const haConfig = {
|
|
211
226
|
...config,
|
|
212
227
|
maxStuckDutiesAgeMs: config.maxStuckDutiesAgeMs ?? epochCache.getL1Constants().slotDuration * 2 * 1000,
|
|
213
228
|
};
|
|
214
|
-
|
|
215
|
-
|
|
229
|
+
({ signer: slashingProtectionSigner } = await createHASigner(haConfig, {
|
|
230
|
+
telemetryClient: telemetry,
|
|
231
|
+
dateProvider,
|
|
232
|
+
}));
|
|
233
|
+
} else {
|
|
234
|
+
// Single-node mode: use LMDB-backed local signing protection.
|
|
235
|
+
// This prevents double-signing if the node crashes and restarts mid-proposal.
|
|
236
|
+
({ signer: slashingProtectionSigner } = await createLocalSignerWithProtection(config, {
|
|
237
|
+
telemetryClient: telemetry,
|
|
238
|
+
dateProvider,
|
|
239
|
+
}));
|
|
216
240
|
}
|
|
241
|
+
const validatorKeyStore: ExtendedValidatorKeyStore = new HAKeyStore(nodeKeystoreAdapter, slashingProtectionSigner);
|
|
217
242
|
|
|
218
243
|
const validator = new ValidatorClient(
|
|
219
244
|
validatorKeyStore,
|
|
@@ -226,6 +251,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
226
251
|
l1ToL2MessageSource,
|
|
227
252
|
config,
|
|
228
253
|
blobClient,
|
|
254
|
+
slashingProtectionSigner,
|
|
229
255
|
dateProvider,
|
|
230
256
|
telemetry,
|
|
231
257
|
);
|
|
@@ -263,6 +289,12 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
263
289
|
this.config = { ...this.config, ...config };
|
|
264
290
|
}
|
|
265
291
|
|
|
292
|
+
public reloadKeystore(newManager: KeystoreManager): void {
|
|
293
|
+
const newAdapter = NodeKeystoreAdapter.fromKeyStoreManager(newManager);
|
|
294
|
+
this.keyStore = new HAKeyStore(newAdapter, this.slashingProtectionSigner);
|
|
295
|
+
this.validationService = new ValidationService(this.keyStore, this.log.createChild('validation-service'));
|
|
296
|
+
}
|
|
297
|
+
|
|
266
298
|
public async start() {
|
|
267
299
|
if (this.epochCacheUpdateLoop.isRunning()) {
|
|
268
300
|
this.log.warn(`Validator client already started`);
|
|
@@ -309,6 +341,16 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
309
341
|
): Promise<CheckpointAttestation[] | undefined> => this.attestToCheckpointProposal(checkpoint, proposalSender);
|
|
310
342
|
this.p2pClient.registerCheckpointProposalHandler(checkpointHandler);
|
|
311
343
|
|
|
344
|
+
// Duplicate proposal handler - triggers slashing for equivocation
|
|
345
|
+
this.p2pClient.registerDuplicateProposalCallback((info: DuplicateProposalInfo) => {
|
|
346
|
+
this.handleDuplicateProposal(info);
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
// Duplicate attestation handler - triggers slashing for attestation equivocation
|
|
350
|
+
this.p2pClient.registerDuplicateAttestationCallback((info: DuplicateAttestationInfo) => {
|
|
351
|
+
this.handleDuplicateAttestation(info);
|
|
352
|
+
});
|
|
353
|
+
|
|
312
354
|
const myAddresses = this.getValidatorAddresses();
|
|
313
355
|
this.p2pClient.registerThisValidatorAddresses(myAddresses);
|
|
314
356
|
|
|
@@ -336,6 +378,15 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
336
378
|
return false;
|
|
337
379
|
}
|
|
338
380
|
|
|
381
|
+
// Ignore proposals from ourselves (may happen in HA setups)
|
|
382
|
+
if (this.getValidatorAddresses().some(addr => addr.equals(proposer))) {
|
|
383
|
+
this.log.debug(`Ignoring block proposal from self for slot ${slotNumber}`, {
|
|
384
|
+
proposer: proposer.toString(),
|
|
385
|
+
slotNumber,
|
|
386
|
+
});
|
|
387
|
+
return false;
|
|
388
|
+
}
|
|
389
|
+
|
|
339
390
|
// Check if we're in the committee (for metrics purposes)
|
|
340
391
|
const inCommittee = await this.epochCache.filterInCommittee(slotNumber, this.getValidatorAddresses());
|
|
341
392
|
const partOfCommittee = inCommittee.length > 0;
|
|
@@ -365,9 +416,10 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
365
416
|
);
|
|
366
417
|
|
|
367
418
|
if (!validationResult.isValid) {
|
|
368
|
-
this.log.warn(`Block proposal validation failed: ${validationResult.reason}`, proposalInfo);
|
|
369
|
-
|
|
370
419
|
const reason = validationResult.reason || 'unknown';
|
|
420
|
+
|
|
421
|
+
this.log.warn(`Block proposal validation failed: ${reason}`, proposalInfo);
|
|
422
|
+
|
|
371
423
|
// Classify failure reason: bad proposal vs node issue
|
|
372
424
|
const badProposalReasons: BlockProposalValidationFailureReason[] = [
|
|
373
425
|
'invalid_proposal',
|
|
@@ -437,6 +489,23 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
437
489
|
return undefined;
|
|
438
490
|
}
|
|
439
491
|
|
|
492
|
+
// Ignore proposals from ourselves (may happen in HA setups)
|
|
493
|
+
if (this.getValidatorAddresses().some(addr => addr.equals(proposer))) {
|
|
494
|
+
this.log.debug(`Ignoring block proposal from self for slot ${slotNumber}`, {
|
|
495
|
+
proposer: proposer.toString(),
|
|
496
|
+
slotNumber,
|
|
497
|
+
});
|
|
498
|
+
return undefined;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
// Validate fee asset price modifier is within allowed range
|
|
502
|
+
if (!validateFeeAssetPriceModifier(proposal.feeAssetPriceModifier)) {
|
|
503
|
+
this.log.warn(
|
|
504
|
+
`Received checkpoint proposal with invalid feeAssetPriceModifier ${proposal.feeAssetPriceModifier} for slot ${slotNumber}`,
|
|
505
|
+
);
|
|
506
|
+
return undefined;
|
|
507
|
+
}
|
|
508
|
+
|
|
440
509
|
// Check that I have any address in current committee before attesting
|
|
441
510
|
const inCommittee = await this.epochCache.filterInCommittee(slotNumber, this.getValidatorAddresses());
|
|
442
511
|
const partOfCommittee = inCommittee.length > 0;
|
|
@@ -445,11 +514,9 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
445
514
|
slotNumber,
|
|
446
515
|
archive: proposal.archive.toString(),
|
|
447
516
|
proposer: proposer.toString(),
|
|
448
|
-
txCount: proposal.txHashes.length,
|
|
449
517
|
};
|
|
450
518
|
this.log.info(`Received checkpoint proposal for slot ${slotNumber}`, {
|
|
451
519
|
...proposalInfo,
|
|
452
|
-
txHashes: proposal.txHashes.map(t => t.toString()),
|
|
453
520
|
fishermanMode: this.config.fishermanMode || false,
|
|
454
521
|
});
|
|
455
522
|
|
|
@@ -485,6 +552,17 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
485
552
|
|
|
486
553
|
this.metrics.incSuccessfulAttestations(inCommittee.length);
|
|
487
554
|
|
|
555
|
+
// Track epoch participation per attester: count each (attester, epoch) pair at most once
|
|
556
|
+
const proposalEpoch = getEpochAtSlot(slotNumber, this.epochCache.getL1Constants());
|
|
557
|
+
for (const attester of inCommittee) {
|
|
558
|
+
const key = attester.toString();
|
|
559
|
+
const lastEpoch = this.lastAttestedEpochByAttester.get(key);
|
|
560
|
+
if (lastEpoch === undefined || proposalEpoch > lastEpoch) {
|
|
561
|
+
this.lastAttestedEpochByAttester.set(key, proposalEpoch);
|
|
562
|
+
this.metrics.incAttestedEpochCount(attester);
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
|
|
488
566
|
// Determine which validators should attest
|
|
489
567
|
let attestors: EthAddress[];
|
|
490
568
|
if (partOfCommittee) {
|
|
@@ -510,15 +588,45 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
510
588
|
return undefined;
|
|
511
589
|
}
|
|
512
590
|
|
|
513
|
-
return this.createCheckpointAttestationsFromProposal(proposal, attestors);
|
|
591
|
+
return await this.createCheckpointAttestationsFromProposal(proposal, attestors);
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
/**
|
|
595
|
+
* Checks if we should attest to a slot based on equivocation prevention rules.
|
|
596
|
+
* @returns true if we should attest, false if we should skip
|
|
597
|
+
*/
|
|
598
|
+
private shouldAttestToSlot(slotNumber: SlotNumber): boolean {
|
|
599
|
+
// If attestToEquivocatedProposals is true, always allow
|
|
600
|
+
if (this.config.attestToEquivocatedProposals) {
|
|
601
|
+
return true;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
// Check if incoming slot is strictly greater than last attested
|
|
605
|
+
if (this.lastAttestedProposal && slotNumber <= this.lastAttestedProposal.slotNumber) {
|
|
606
|
+
this.log.warn(
|
|
607
|
+
`Refusing to process a proposal for slot ${slotNumber} given we already attested to a proposal for slot ${this.lastAttestedProposal.slotNumber}`,
|
|
608
|
+
);
|
|
609
|
+
return false;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
return true;
|
|
514
613
|
}
|
|
515
614
|
|
|
516
615
|
private async createCheckpointAttestationsFromProposal(
|
|
517
616
|
proposal: CheckpointProposalCore,
|
|
518
617
|
attestors: EthAddress[] = [],
|
|
519
|
-
): Promise<CheckpointAttestation[]> {
|
|
618
|
+
): Promise<CheckpointAttestation[] | undefined> {
|
|
619
|
+
// Equivocation check: must happen right before signing to minimize the race window
|
|
620
|
+
if (!this.shouldAttestToSlot(proposal.slotNumber)) {
|
|
621
|
+
return undefined;
|
|
622
|
+
}
|
|
623
|
+
|
|
520
624
|
const attestations = await this.validationService.attestToCheckpointProposal(proposal, attestors);
|
|
521
|
-
|
|
625
|
+
|
|
626
|
+
// Track the proposal we attested to (to prevent equivocation)
|
|
627
|
+
this.lastAttestedProposal = proposal;
|
|
628
|
+
|
|
629
|
+
await this.p2pClient.addOwnCheckpointAttestations(attestations);
|
|
522
630
|
return attestations;
|
|
523
631
|
}
|
|
524
632
|
|
|
@@ -531,7 +639,11 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
531
639
|
proposalInfo: LogData,
|
|
532
640
|
): Promise<{ isValid: true } | { isValid: false; reason: string }> {
|
|
533
641
|
const slot = proposal.slotNumber;
|
|
534
|
-
|
|
642
|
+
|
|
643
|
+
// Timeout block syncing at the start of the next slot
|
|
644
|
+
const config = this.checkpointsBuilder.getConfig();
|
|
645
|
+
const nextSlotTimestampSeconds = Number(getTimestampForSlot(SlotNumber(slot + 1), config));
|
|
646
|
+
const timeoutSeconds = Math.max(1, nextSlotTimestampSeconds - Math.floor(this.dateProvider.now() / 1000));
|
|
535
647
|
|
|
536
648
|
// Wait for last block to sync by archive
|
|
537
649
|
let lastBlockHeader: BlockHeader | undefined;
|
|
@@ -566,6 +678,12 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
566
678
|
return { isValid: false, reason: 'no_blocks_for_slot' };
|
|
567
679
|
}
|
|
568
680
|
|
|
681
|
+
// Ensure the last block for this slot matches the archive in the checkpoint proposal
|
|
682
|
+
if (!blocks.at(-1)?.archive.root.equals(proposal.archive)) {
|
|
683
|
+
this.log.warn(`Last block archive mismatch for checkpoint proposal`, proposalInfo);
|
|
684
|
+
return { isValid: false, reason: 'last_block_archive_mismatch' };
|
|
685
|
+
}
|
|
686
|
+
|
|
569
687
|
this.log.debug(`Found ${blocks.length} blocks for slot ${slot}`, {
|
|
570
688
|
...proposalInfo,
|
|
571
689
|
blockNumbers: blocks.map(b => b.number),
|
|
@@ -579,14 +697,11 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
579
697
|
// Get L1-to-L2 messages for this checkpoint
|
|
580
698
|
const l1ToL2Messages = await this.l1ToL2MessageSource.getL1ToL2Messages(checkpointNumber);
|
|
581
699
|
|
|
582
|
-
//
|
|
583
|
-
// TODO: There can be a more efficient way to get the previous checkpoint out hashes without having to fetch the
|
|
584
|
-
// actual checkpoints and the blocks/txs in them.
|
|
700
|
+
// Collect the out hashes of all the checkpoints before this one in the same epoch
|
|
585
701
|
const epoch = getEpochAtSlot(slot, this.epochCache.getL1Constants());
|
|
586
|
-
const
|
|
587
|
-
.filter(
|
|
588
|
-
.
|
|
589
|
-
const previousCheckpointOutHashes = previousCheckpoints.map(c => c.getCheckpointOutHash());
|
|
702
|
+
const previousCheckpointOutHashes = (await this.blockSource.getCheckpointsDataForEpoch(epoch))
|
|
703
|
+
.filter(c => c.checkpointNumber < checkpointNumber)
|
|
704
|
+
.map(c => c.checkpointOutHash);
|
|
590
705
|
|
|
591
706
|
// Fork world state at the block before the first block
|
|
592
707
|
const parentBlockNumber = BlockNumber(firstBlock.number - 1);
|
|
@@ -597,6 +712,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
597
712
|
const checkpointBuilder = await this.checkpointsBuilder.openCheckpoint(
|
|
598
713
|
checkpointNumber,
|
|
599
714
|
constants,
|
|
715
|
+
proposal.feeAssetPriceModifier,
|
|
600
716
|
l1ToL2Messages,
|
|
601
717
|
previousCheckpointOutHashes,
|
|
602
718
|
fork,
|
|
@@ -643,6 +759,20 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
643
759
|
return { isValid: false, reason: 'out_hash_mismatch' };
|
|
644
760
|
}
|
|
645
761
|
|
|
762
|
+
// Final round of validations on the checkpoint, just in case.
|
|
763
|
+
try {
|
|
764
|
+
validateCheckpoint(computedCheckpoint, {
|
|
765
|
+
rollupManaLimit: this.checkpointsBuilder.getConfig().rollupManaLimit,
|
|
766
|
+
maxDABlockGas: this.config.validateMaxDABlockGas,
|
|
767
|
+
maxL2BlockGas: this.config.validateMaxL2BlockGas,
|
|
768
|
+
maxTxsPerBlock: this.config.validateMaxTxsPerBlock,
|
|
769
|
+
maxTxsPerCheckpoint: this.config.validateMaxTxsPerCheckpoint,
|
|
770
|
+
});
|
|
771
|
+
} catch (err) {
|
|
772
|
+
this.log.warn(`Checkpoint validation failed: ${err}`, proposalInfo);
|
|
773
|
+
return { isValid: false, reason: 'checkpoint_validation_failed' };
|
|
774
|
+
}
|
|
775
|
+
|
|
646
776
|
this.log.verbose(`Checkpoint proposal validation successful for slot ${slot}`, proposalInfo);
|
|
647
777
|
return { isValid: true };
|
|
648
778
|
} finally {
|
|
@@ -659,6 +789,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
659
789
|
chainId: gv.chainId,
|
|
660
790
|
version: gv.version,
|
|
661
791
|
slotNumber: gv.slotNumber,
|
|
792
|
+
timestamp: gv.timestamp,
|
|
662
793
|
coinbase: gv.coinbase,
|
|
663
794
|
feeRecipient: gv.feeRecipient,
|
|
664
795
|
gasFees: gv.gasFees,
|
|
@@ -668,7 +799,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
668
799
|
/**
|
|
669
800
|
* Uploads blobs for a checkpoint to the filestore (fire and forget).
|
|
670
801
|
*/
|
|
671
|
-
|
|
802
|
+
protected async uploadBlobsForCheckpoint(proposal: CheckpointProposalCore, proposalInfo: LogData): Promise<void> {
|
|
672
803
|
try {
|
|
673
804
|
const lastBlockHeader = await this.blockSource.getBlockHeaderByArchive(proposal.archive);
|
|
674
805
|
if (!lastBlockHeader) {
|
|
@@ -683,7 +814,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
683
814
|
}
|
|
684
815
|
|
|
685
816
|
const blobFields = blocks.flatMap(b => b.toBlobFields());
|
|
686
|
-
const blobs: Blob[] = getBlobsPerL1Block(blobFields);
|
|
817
|
+
const blobs: Blob[] = await getBlobsPerL1Block(blobFields);
|
|
687
818
|
await this.blobClient.sendBlobsToFilestore(blobs);
|
|
688
819
|
this.log.debug(`Uploaded ${blobs.length} blobs to filestore for checkpoint at slot ${proposal.slotNumber}`, {
|
|
689
820
|
...proposalInfo,
|
|
@@ -721,6 +852,52 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
721
852
|
]);
|
|
722
853
|
}
|
|
723
854
|
|
|
855
|
+
/**
|
|
856
|
+
* Handle detection of a duplicate proposal (equivocation).
|
|
857
|
+
* Emits a slash event when a proposer sends multiple proposals for the same position.
|
|
858
|
+
*/
|
|
859
|
+
private handleDuplicateProposal(info: DuplicateProposalInfo): void {
|
|
860
|
+
const { slot, proposer, type } = info;
|
|
861
|
+
|
|
862
|
+
this.log.warn(`Triggering slash event for duplicate ${type} proposal from ${proposer.toString()} at slot ${slot}`, {
|
|
863
|
+
proposer: proposer.toString(),
|
|
864
|
+
slot,
|
|
865
|
+
type,
|
|
866
|
+
});
|
|
867
|
+
|
|
868
|
+
// Emit slash event
|
|
869
|
+
this.emit(WANT_TO_SLASH_EVENT, [
|
|
870
|
+
{
|
|
871
|
+
validator: proposer,
|
|
872
|
+
amount: this.config.slashDuplicateProposalPenalty,
|
|
873
|
+
offenseType: OffenseType.DUPLICATE_PROPOSAL,
|
|
874
|
+
epochOrSlot: BigInt(slot),
|
|
875
|
+
},
|
|
876
|
+
]);
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
/**
|
|
880
|
+
* Handle detection of a duplicate attestation (equivocation).
|
|
881
|
+
* Emits a slash event when an attester signs attestations for different proposals at the same slot.
|
|
882
|
+
*/
|
|
883
|
+
private handleDuplicateAttestation(info: DuplicateAttestationInfo): void {
|
|
884
|
+
const { slot, attester } = info;
|
|
885
|
+
|
|
886
|
+
this.log.warn(`Triggering slash event for duplicate attestation from ${attester.toString()} at slot ${slot}`, {
|
|
887
|
+
attester: attester.toString(),
|
|
888
|
+
slot,
|
|
889
|
+
});
|
|
890
|
+
|
|
891
|
+
this.emit(WANT_TO_SLASH_EVENT, [
|
|
892
|
+
{
|
|
893
|
+
validator: attester,
|
|
894
|
+
amount: this.config.slashDuplicateAttestationPenalty,
|
|
895
|
+
offenseType: OffenseType.DUPLICATE_ATTESTATION,
|
|
896
|
+
epochOrSlot: BigInt(slot),
|
|
897
|
+
},
|
|
898
|
+
]);
|
|
899
|
+
}
|
|
900
|
+
|
|
724
901
|
async createBlockProposal(
|
|
725
902
|
blockHeader: BlockHeader,
|
|
726
903
|
indexWithinCheckpoint: IndexWithinCheckpoint,
|
|
@@ -730,11 +907,19 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
730
907
|
proposerAddress: EthAddress | undefined,
|
|
731
908
|
options: BlockProposalOptions = {},
|
|
732
909
|
): Promise<BlockProposal> {
|
|
733
|
-
//
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
910
|
+
// Validate that we're not creating a proposal for an older or equal position
|
|
911
|
+
if (this.lastProposedBlock) {
|
|
912
|
+
const lastSlot = this.lastProposedBlock.slotNumber;
|
|
913
|
+
const lastIndex = this.lastProposedBlock.indexWithinCheckpoint;
|
|
914
|
+
const newSlot = blockHeader.globalVariables.slotNumber;
|
|
915
|
+
|
|
916
|
+
if (newSlot < lastSlot || (newSlot === lastSlot && indexWithinCheckpoint <= lastIndex)) {
|
|
917
|
+
throw new Error(
|
|
918
|
+
`Cannot create block proposal for slot ${newSlot} index ${indexWithinCheckpoint}: ` +
|
|
919
|
+
`already proposed block for slot ${lastSlot} index ${lastIndex}`,
|
|
920
|
+
);
|
|
921
|
+
}
|
|
922
|
+
}
|
|
738
923
|
|
|
739
924
|
this.log.info(
|
|
740
925
|
`Assembling block proposal for block ${blockHeader.globalVariables.blockNumber} slot ${blockHeader.globalVariables.slotNumber}`,
|
|
@@ -751,25 +936,42 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
751
936
|
broadcastInvalidBlockProposal: this.config.broadcastInvalidBlockProposal,
|
|
752
937
|
},
|
|
753
938
|
);
|
|
754
|
-
this.
|
|
939
|
+
this.lastProposedBlock = newProposal;
|
|
755
940
|
return newProposal;
|
|
756
941
|
}
|
|
757
942
|
|
|
758
943
|
async createCheckpointProposal(
|
|
759
944
|
checkpointHeader: CheckpointHeader,
|
|
760
945
|
archive: Fr,
|
|
946
|
+
feeAssetPriceModifier: bigint,
|
|
761
947
|
lastBlockInfo: CreateCheckpointProposalLastBlockData | undefined,
|
|
762
948
|
proposerAddress: EthAddress | undefined,
|
|
763
949
|
options: CheckpointProposalOptions = {},
|
|
764
950
|
): Promise<CheckpointProposal> {
|
|
951
|
+
// Validate that we're not creating a proposal for an older or equal slot
|
|
952
|
+
if (this.lastProposedCheckpoint) {
|
|
953
|
+
const lastSlot = this.lastProposedCheckpoint.slotNumber;
|
|
954
|
+
const newSlot = checkpointHeader.slotNumber;
|
|
955
|
+
|
|
956
|
+
if (newSlot <= lastSlot) {
|
|
957
|
+
throw new Error(
|
|
958
|
+
`Cannot create checkpoint proposal for slot ${newSlot}: ` +
|
|
959
|
+
`already proposed checkpoint for slot ${lastSlot}`,
|
|
960
|
+
);
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
|
|
765
964
|
this.log.info(`Assembling checkpoint proposal for slot ${checkpointHeader.slotNumber}`);
|
|
766
|
-
|
|
965
|
+
const newProposal = await this.validationService.createCheckpointProposal(
|
|
767
966
|
checkpointHeader,
|
|
768
967
|
archive,
|
|
968
|
+
feeAssetPriceModifier,
|
|
769
969
|
lastBlockInfo,
|
|
770
970
|
proposerAddress,
|
|
771
971
|
options,
|
|
772
972
|
);
|
|
973
|
+
this.lastProposedCheckpoint = newProposal;
|
|
974
|
+
return newProposal;
|
|
773
975
|
}
|
|
774
976
|
|
|
775
977
|
async broadcastBlockProposal(proposal: BlockProposal): Promise<void> {
|
|
@@ -791,6 +993,10 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
791
993
|
this.log.debug(`Collecting ${inCommittee.length} self-attestations for slot ${slot}`, { inCommittee });
|
|
792
994
|
const attestations = await this.createCheckpointAttestationsFromProposal(proposal, inCommittee);
|
|
793
995
|
|
|
996
|
+
if (!attestations) {
|
|
997
|
+
return [];
|
|
998
|
+
}
|
|
999
|
+
|
|
794
1000
|
// We broadcast our own attestations to our peers so, in case our block does not get mined on L1,
|
|
795
1001
|
// other nodes can see that our validators did attest to this block proposal, and do not slash us
|
|
796
1002
|
// 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"}
|