@aztec/validator-client 0.0.1-commit.f295ac2 → 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 +22 -19
- package/dest/block_proposal_handler.d.ts +6 -8
- package/dest/block_proposal_handler.d.ts.map +1 -1
- package/dest/block_proposal_handler.js +28 -57
- package/dest/checkpoint_builder.d.ts +15 -13
- package/dest/checkpoint_builder.d.ts.map +1 -1
- package/dest/checkpoint_builder.js +55 -32
- package/dest/config.d.ts +1 -1
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +9 -7
- 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/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 +12 -3
- package/dest/metrics.d.ts.map +1 -1
- package/dest/metrics.js +46 -5
- package/dest/validator.d.ts +40 -14
- package/dest/validator.d.ts.map +1 -1
- package/dest/validator.js +196 -52
- package/package.json +19 -17
- package/src/block_proposal_handler.ts +40 -81
- package/src/checkpoint_builder.ts +80 -33
- package/src/config.ts +9 -7
- package/src/duties/validation_service.ts +9 -2
- package/src/factory.ts +1 -0
- package/src/index.ts +0 -1
- package/src/key_store/ha_key_store.ts +2 -2
- package/src/metrics.ts +63 -6
- package/src/validator.ts +253 -65
- 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 -53
- 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 -133
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
|
-
import type { CommitteeAttestationsAndSigners,
|
|
26
|
-
import { getEpochAtSlot } from '@aztec/stdlib/epoch-helpers';
|
|
26
|
+
import type { CommitteeAttestationsAndSigners, L2Block, L2BlockSink, L2BlockSource } from '@aztec/stdlib/block';
|
|
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,24 @@ 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;
|
|
92
|
+
/** Tracks the last epoch in which each attester successfully submitted at least one attestation. */
|
|
93
|
+
private lastAttestedEpochByAttester: Map<string, EpochNumber> = new Map();
|
|
87
94
|
|
|
88
95
|
private proposersOfInvalidBlocks: Set<string> = new Set();
|
|
89
96
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
// eslint-disable-next-line aztec-custom/no-non-primitive-in-collections
|
|
93
|
-
private validatedBlockSlots: Set<SlotNumber> = new Set();
|
|
97
|
+
/** Tracks the last checkpoint proposal we attested to, to prevent equivocation. */
|
|
98
|
+
private lastAttestedProposal?: CheckpointProposalCore;
|
|
94
99
|
|
|
95
100
|
protected constructor(
|
|
96
101
|
private keyStore: ExtendedValidatorKeyStore,
|
|
@@ -103,6 +108,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
103
108
|
private l1ToL2MessageSource: L1ToL2MessageSource,
|
|
104
109
|
private config: ValidatorClientFullConfig,
|
|
105
110
|
private blobClient: BlobClientInterface,
|
|
111
|
+
private haSigner: ValidatorHASigner | undefined,
|
|
106
112
|
private dateProvider: DateProvider = new DateProvider(),
|
|
107
113
|
telemetry: TelemetryClient = getTelemetryClient(),
|
|
108
114
|
log = createLogger('validator'),
|
|
@@ -156,6 +162,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
156
162
|
this.log.trace(`No committee found for slot`);
|
|
157
163
|
return;
|
|
158
164
|
}
|
|
165
|
+
this.metrics.setCurrentEpoch(epoch);
|
|
159
166
|
if (epoch !== this.lastEpochForCommitteeUpdateLoop) {
|
|
160
167
|
const me = this.getValidatorAddresses();
|
|
161
168
|
const committeeSet = new Set(committee.map(v => v.toString()));
|
|
@@ -184,7 +191,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
184
191
|
p2pClient: P2P,
|
|
185
192
|
blockSource: L2BlockSource & L2BlockSink,
|
|
186
193
|
l1ToL2MessageSource: L1ToL2MessageSource,
|
|
187
|
-
txProvider:
|
|
194
|
+
txProvider: ITxProvider,
|
|
188
195
|
keyStoreManager: KeystoreManager,
|
|
189
196
|
blobClient: BlobClientInterface,
|
|
190
197
|
dateProvider: DateProvider = new DateProvider(),
|
|
@@ -193,6 +200,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
193
200
|
const metrics = new ValidatorMetrics(telemetry);
|
|
194
201
|
const blockProposalValidator = new BlockProposalValidator(epochCache, {
|
|
195
202
|
txsPermitted: !config.disableTransactions,
|
|
203
|
+
maxTxsPerBlock: config.maxTxsPerBlock,
|
|
196
204
|
});
|
|
197
205
|
const blockProposalHandler = new BlockProposalHandler(
|
|
198
206
|
checkpointsBuilder,
|
|
@@ -208,15 +216,18 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
208
216
|
telemetry,
|
|
209
217
|
);
|
|
210
218
|
|
|
211
|
-
|
|
219
|
+
const nodeKeystoreAdapter = NodeKeystoreAdapter.fromKeyStoreManager(keyStoreManager);
|
|
220
|
+
let validatorKeyStore: ExtendedValidatorKeyStore = nodeKeystoreAdapter;
|
|
221
|
+
let haSigner: ValidatorHASigner | undefined;
|
|
212
222
|
if (config.haSigningEnabled) {
|
|
213
223
|
// If maxStuckDutiesAgeMs is not explicitly set, compute it from Aztec slot duration
|
|
214
224
|
const haConfig = {
|
|
215
225
|
...config,
|
|
216
226
|
maxStuckDutiesAgeMs: config.maxStuckDutiesAgeMs ?? epochCache.getL1Constants().slotDuration * 2 * 1000,
|
|
217
227
|
};
|
|
218
|
-
const { signer } = await createHASigner(haConfig);
|
|
219
|
-
|
|
228
|
+
const { signer } = await createHASigner(haConfig, { telemetryClient: telemetry, dateProvider });
|
|
229
|
+
haSigner = signer;
|
|
230
|
+
validatorKeyStore = new HAKeyStore(nodeKeystoreAdapter, signer);
|
|
220
231
|
}
|
|
221
232
|
|
|
222
233
|
const validator = new ValidatorClient(
|
|
@@ -230,6 +241,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
230
241
|
l1ToL2MessageSource,
|
|
231
242
|
config,
|
|
232
243
|
blobClient,
|
|
244
|
+
haSigner,
|
|
233
245
|
dateProvider,
|
|
234
246
|
telemetry,
|
|
235
247
|
);
|
|
@@ -267,6 +279,28 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
267
279
|
this.config = { ...this.config, ...config };
|
|
268
280
|
}
|
|
269
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
|
+
|
|
270
304
|
public async start() {
|
|
271
305
|
if (this.epochCacheUpdateLoop.isRunning()) {
|
|
272
306
|
this.log.warn(`Validator client already started`);
|
|
@@ -313,6 +347,16 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
313
347
|
): Promise<CheckpointAttestation[] | undefined> => this.attestToCheckpointProposal(checkpoint, proposalSender);
|
|
314
348
|
this.p2pClient.registerCheckpointProposalHandler(checkpointHandler);
|
|
315
349
|
|
|
350
|
+
// Duplicate proposal handler - triggers slashing for equivocation
|
|
351
|
+
this.p2pClient.registerDuplicateProposalCallback((info: DuplicateProposalInfo) => {
|
|
352
|
+
this.handleDuplicateProposal(info);
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
// Duplicate attestation handler - triggers slashing for attestation equivocation
|
|
356
|
+
this.p2pClient.registerDuplicateAttestationCallback((info: DuplicateAttestationInfo) => {
|
|
357
|
+
this.handleDuplicateAttestation(info);
|
|
358
|
+
});
|
|
359
|
+
|
|
316
360
|
const myAddresses = this.getValidatorAddresses();
|
|
317
361
|
this.p2pClient.registerThisValidatorAddresses(myAddresses);
|
|
318
362
|
|
|
@@ -340,6 +384,15 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
340
384
|
return false;
|
|
341
385
|
}
|
|
342
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
|
+
|
|
343
396
|
// Check if we're in the committee (for metrics purposes)
|
|
344
397
|
const inCommittee = await this.epochCache.filterInCommittee(slotNumber, this.getValidatorAddresses());
|
|
345
398
|
const partOfCommittee = inCommittee.length > 0;
|
|
@@ -413,10 +466,6 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
413
466
|
return false;
|
|
414
467
|
}
|
|
415
468
|
|
|
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
469
|
return true;
|
|
421
470
|
}
|
|
422
471
|
|
|
@@ -445,6 +494,23 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
445
494
|
return undefined;
|
|
446
495
|
}
|
|
447
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
|
+
|
|
448
514
|
// Check that I have any address in current committee before attesting
|
|
449
515
|
const inCommittee = await this.epochCache.filterInCommittee(slotNumber, this.getValidatorAddresses());
|
|
450
516
|
const partOfCommittee = inCommittee.length > 0;
|
|
@@ -461,17 +527,9 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
461
527
|
fishermanMode: this.config.fishermanMode || false,
|
|
462
528
|
});
|
|
463
529
|
|
|
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
530
|
// Validate the checkpoint proposal before attesting (unless skipCheckpointProposalValidation is set)
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
this.log.verbose(`Skipping checkpoint proposal validation for slot ${slotNumber}`, proposalInfo);
|
|
531
|
+
if (this.config.skipCheckpointProposalValidation) {
|
|
532
|
+
this.log.warn(`Skipping checkpoint proposal validation for slot ${slotNumber}`, proposalInfo);
|
|
475
533
|
} else {
|
|
476
534
|
const validationResult = await this.validateCheckpointProposal(proposal, proposalInfo);
|
|
477
535
|
if (!validationResult.isValid) {
|
|
@@ -501,6 +559,17 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
501
559
|
|
|
502
560
|
this.metrics.incSuccessfulAttestations(inCommittee.length);
|
|
503
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
|
+
|
|
504
573
|
// Determine which validators should attest
|
|
505
574
|
let attestors: EthAddress[];
|
|
506
575
|
if (partOfCommittee) {
|
|
@@ -526,15 +595,45 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
526
595
|
return undefined;
|
|
527
596
|
}
|
|
528
597
|
|
|
529
|
-
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;
|
|
530
620
|
}
|
|
531
621
|
|
|
532
622
|
private async createCheckpointAttestationsFromProposal(
|
|
533
623
|
proposal: CheckpointProposalCore,
|
|
534
624
|
attestors: EthAddress[] = [],
|
|
535
|
-
): 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
|
+
|
|
536
631
|
const attestations = await this.validationService.attestToCheckpointProposal(proposal, attestors);
|
|
537
|
-
|
|
632
|
+
|
|
633
|
+
// Track the proposal we attested to (to prevent equivocation)
|
|
634
|
+
this.lastAttestedProposal = proposal;
|
|
635
|
+
|
|
636
|
+
await this.p2pClient.addOwnCheckpointAttestations(attestations);
|
|
538
637
|
return attestations;
|
|
539
638
|
}
|
|
540
639
|
|
|
@@ -547,7 +646,11 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
547
646
|
proposalInfo: LogData,
|
|
548
647
|
): Promise<{ isValid: true } | { isValid: false; reason: string }> {
|
|
549
648
|
const slot = proposal.slotNumber;
|
|
550
|
-
|
|
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));
|
|
551
654
|
|
|
552
655
|
// Wait for last block to sync by archive
|
|
553
656
|
let lastBlockHeader: BlockHeader | undefined;
|
|
@@ -582,6 +685,12 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
582
685
|
return { isValid: false, reason: 'no_blocks_for_slot' };
|
|
583
686
|
}
|
|
584
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
|
+
|
|
585
694
|
this.log.debug(`Found ${blocks.length} blocks for slot ${slot}`, {
|
|
586
695
|
...proposalInfo,
|
|
587
696
|
blockNumbers: blocks.map(b => b.number),
|
|
@@ -595,14 +704,11 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
595
704
|
// Get L1-to-L2 messages for this checkpoint
|
|
596
705
|
const l1ToL2Messages = await this.l1ToL2MessageSource.getL1ToL2Messages(checkpointNumber);
|
|
597
706
|
|
|
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.
|
|
707
|
+
// Collect the out hashes of all the checkpoints before this one in the same epoch
|
|
601
708
|
const epoch = getEpochAtSlot(slot, this.epochCache.getL1Constants());
|
|
602
|
-
const
|
|
603
|
-
.filter(
|
|
604
|
-
.
|
|
605
|
-
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);
|
|
606
712
|
|
|
607
713
|
// Fork world state at the block before the first block
|
|
608
714
|
const parentBlockNumber = BlockNumber(firstBlock.number - 1);
|
|
@@ -613,10 +719,12 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
613
719
|
const checkpointBuilder = await this.checkpointsBuilder.openCheckpoint(
|
|
614
720
|
checkpointNumber,
|
|
615
721
|
constants,
|
|
722
|
+
proposal.feeAssetPriceModifier,
|
|
616
723
|
l1ToL2Messages,
|
|
617
724
|
previousCheckpointOutHashes,
|
|
618
725
|
fork,
|
|
619
726
|
blocks,
|
|
727
|
+
this.log.getBindings(),
|
|
620
728
|
);
|
|
621
729
|
|
|
622
730
|
// Complete the checkpoint to get computed values
|
|
@@ -642,13 +750,17 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
642
750
|
return { isValid: false, reason: 'archive_mismatch' };
|
|
643
751
|
}
|
|
644
752
|
|
|
645
|
-
// Check that the accumulated out hash matches the value in the proposal.
|
|
646
|
-
|
|
647
|
-
const
|
|
648
|
-
|
|
753
|
+
// Check that the accumulated epoch out hash matches the value in the proposal.
|
|
754
|
+
// The epoch out hash is the accumulated hash of all checkpoint out hashes in the epoch.
|
|
755
|
+
const checkpointOutHash = computedCheckpoint.getCheckpointOutHash();
|
|
756
|
+
const computedEpochOutHash = accumulateCheckpointOutHashes([...previousCheckpointOutHashes, checkpointOutHash]);
|
|
757
|
+
const proposalEpochOutHash = proposal.checkpointHeader.epochOutHash;
|
|
758
|
+
if (!computedEpochOutHash.equals(proposalEpochOutHash)) {
|
|
649
759
|
this.log.warn(`Epoch out hash mismatch`, {
|
|
650
|
-
|
|
651
|
-
|
|
760
|
+
proposalEpochOutHash: proposalEpochOutHash.toString(),
|
|
761
|
+
computedEpochOutHash: computedEpochOutHash.toString(),
|
|
762
|
+
checkpointOutHash: checkpointOutHash.toString(),
|
|
763
|
+
previousCheckpointOutHashes: previousCheckpointOutHashes.map(h => h.toString()),
|
|
652
764
|
...proposalInfo,
|
|
653
765
|
});
|
|
654
766
|
return { isValid: false, reason: 'out_hash_mismatch' };
|
|
@@ -664,12 +776,13 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
664
776
|
/**
|
|
665
777
|
* Extract checkpoint global variables from a block.
|
|
666
778
|
*/
|
|
667
|
-
private extractCheckpointConstants(block:
|
|
779
|
+
private extractCheckpointConstants(block: L2Block): CheckpointGlobalVariables {
|
|
668
780
|
const gv = block.header.globalVariables;
|
|
669
781
|
return {
|
|
670
782
|
chainId: gv.chainId,
|
|
671
783
|
version: gv.version,
|
|
672
784
|
slotNumber: gv.slotNumber,
|
|
785
|
+
timestamp: gv.timestamp,
|
|
673
786
|
coinbase: gv.coinbase,
|
|
674
787
|
feeRecipient: gv.feeRecipient,
|
|
675
788
|
gasFees: gv.gasFees,
|
|
@@ -679,7 +792,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
679
792
|
/**
|
|
680
793
|
* Uploads blobs for a checkpoint to the filestore (fire and forget).
|
|
681
794
|
*/
|
|
682
|
-
|
|
795
|
+
protected async uploadBlobsForCheckpoint(proposal: CheckpointProposalCore, proposalInfo: LogData): Promise<void> {
|
|
683
796
|
try {
|
|
684
797
|
const lastBlockHeader = await this.blockSource.getBlockHeaderByArchive(proposal.archive);
|
|
685
798
|
if (!lastBlockHeader) {
|
|
@@ -694,7 +807,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
694
807
|
}
|
|
695
808
|
|
|
696
809
|
const blobFields = blocks.flatMap(b => b.toBlobFields());
|
|
697
|
-
const blobs: Blob[] = getBlobsPerL1Block(blobFields);
|
|
810
|
+
const blobs: Blob[] = await getBlobsPerL1Block(blobFields);
|
|
698
811
|
await this.blobClient.sendBlobsToFilestore(blobs);
|
|
699
812
|
this.log.debug(`Uploaded ${blobs.length} blobs to filestore for checkpoint at slot ${proposal.slotNumber}`, {
|
|
700
813
|
...proposalInfo,
|
|
@@ -732,6 +845,52 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
732
845
|
]);
|
|
733
846
|
}
|
|
734
847
|
|
|
848
|
+
/**
|
|
849
|
+
* Handle detection of a duplicate proposal (equivocation).
|
|
850
|
+
* Emits a slash event when a proposer sends multiple proposals for the same position.
|
|
851
|
+
*/
|
|
852
|
+
private handleDuplicateProposal(info: DuplicateProposalInfo): void {
|
|
853
|
+
const { slot, proposer, type } = info;
|
|
854
|
+
|
|
855
|
+
this.log.warn(`Triggering slash event for duplicate ${type} proposal from ${proposer.toString()} at slot ${slot}`, {
|
|
856
|
+
proposer: proposer.toString(),
|
|
857
|
+
slot,
|
|
858
|
+
type,
|
|
859
|
+
});
|
|
860
|
+
|
|
861
|
+
// Emit slash event
|
|
862
|
+
this.emit(WANT_TO_SLASH_EVENT, [
|
|
863
|
+
{
|
|
864
|
+
validator: proposer,
|
|
865
|
+
amount: this.config.slashDuplicateProposalPenalty,
|
|
866
|
+
offenseType: OffenseType.DUPLICATE_PROPOSAL,
|
|
867
|
+
epochOrSlot: BigInt(slot),
|
|
868
|
+
},
|
|
869
|
+
]);
|
|
870
|
+
}
|
|
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
|
+
|
|
735
894
|
async createBlockProposal(
|
|
736
895
|
blockHeader: BlockHeader,
|
|
737
896
|
indexWithinCheckpoint: IndexWithinCheckpoint,
|
|
@@ -739,13 +898,21 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
739
898
|
archive: Fr,
|
|
740
899
|
txs: Tx[],
|
|
741
900
|
proposerAddress: EthAddress | undefined,
|
|
742
|
-
options: BlockProposalOptions,
|
|
901
|
+
options: BlockProposalOptions = {},
|
|
743
902
|
): Promise<BlockProposal> {
|
|
744
|
-
//
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
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
|
+
}
|
|
749
916
|
|
|
750
917
|
this.log.info(
|
|
751
918
|
`Assembling block proposal for block ${blockHeader.globalVariables.blockNumber} slot ${blockHeader.globalVariables.slotNumber}`,
|
|
@@ -762,25 +929,42 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
762
929
|
broadcastInvalidBlockProposal: this.config.broadcastInvalidBlockProposal,
|
|
763
930
|
},
|
|
764
931
|
);
|
|
765
|
-
this.
|
|
932
|
+
this.lastProposedBlock = newProposal;
|
|
766
933
|
return newProposal;
|
|
767
934
|
}
|
|
768
935
|
|
|
769
936
|
async createCheckpointProposal(
|
|
770
937
|
checkpointHeader: CheckpointHeader,
|
|
771
938
|
archive: Fr,
|
|
939
|
+
feeAssetPriceModifier: bigint,
|
|
772
940
|
lastBlockInfo: CreateCheckpointProposalLastBlockData | undefined,
|
|
773
941
|
proposerAddress: EthAddress | undefined,
|
|
774
|
-
options: CheckpointProposalOptions,
|
|
942
|
+
options: CheckpointProposalOptions = {},
|
|
775
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
|
+
|
|
776
957
|
this.log.info(`Assembling checkpoint proposal for slot ${checkpointHeader.slotNumber}`);
|
|
777
|
-
|
|
958
|
+
const newProposal = await this.validationService.createCheckpointProposal(
|
|
778
959
|
checkpointHeader,
|
|
779
960
|
archive,
|
|
961
|
+
feeAssetPriceModifier,
|
|
780
962
|
lastBlockInfo,
|
|
781
963
|
proposerAddress,
|
|
782
964
|
options,
|
|
783
965
|
);
|
|
966
|
+
this.lastProposedCheckpoint = newProposal;
|
|
967
|
+
return newProposal;
|
|
784
968
|
}
|
|
785
969
|
|
|
786
970
|
async broadcastBlockProposal(proposal: BlockProposal): Promise<void> {
|
|
@@ -802,6 +986,10 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
802
986
|
this.log.debug(`Collecting ${inCommittee.length} self-attestations for slot ${slot}`, { inCommittee });
|
|
803
987
|
const attestations = await this.createCheckpointAttestationsFromProposal(proposal, inCommittee);
|
|
804
988
|
|
|
989
|
+
if (!attestations) {
|
|
990
|
+
return [];
|
|
991
|
+
}
|
|
992
|
+
|
|
805
993
|
// We broadcast our own attestations to our peers so, in case our block does not get mined on L1,
|
|
806
994
|
// other nodes can see that our validators did attest to this block proposal, and do not slash us
|
|
807
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,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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHhfdmFsaWRhdG9yX2ZhY3RvcnkuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy90eF92YWxpZGF0b3IvdHhfdmFsaWRhdG9yX2ZhY3RvcnkudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLGlDQUFpQyxDQUFDO0FBaUI5RCxPQUFPLEtBQUssRUFBRSxrQkFBa0IsRUFBRSxNQUFNLHdCQUF3QixDQUFDO0FBQ2pFLE9BQU8sS0FBSyxFQUFFLE9BQU8sRUFBRSxNQUFNLG1CQUFtQixDQUFDO0FBQ2pELE9BQU8sS0FBSyxFQUNWLGNBQWMsRUFDZCw2QkFBNkIsRUFDN0Isd0JBQXdCLEVBQ3hCLHdCQUF3QixFQUN6QixNQUFNLGlDQUFpQyxDQUFDO0FBRXpDLE9BQU8sRUFBRSxlQUFlLEVBQUUsS0FBSyxFQUFFLEVBQUUsS0FBSyxXQUFXLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUM5RSxPQUFPLEtBQUssRUFBRSxNQUFNLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUlsRCx3QkFBZ0IsOEJBQThCLENBQzVDLEVBQUUsRUFBRSx3QkFBd0IsRUFDNUIsa0JBQWtCLEVBQUUsa0JBQWtCLEVBQ3RDLFFBQVEsRUFBRSw2QkFBNkIsR0FBRyxTQUFTLEVBQ25ELEVBQ0UsU0FBUyxFQUNULGFBQWEsRUFDYixjQUFjLEVBQ2QsT0FBTyxFQUNQLGtCQUFrQixFQUNsQixTQUFTLEVBQ1QsV0FBVyxFQUNYLFlBQVksRUFDYixFQUFFO0lBQ0QsU0FBUyxFQUFFLE1BQU0sQ0FBQztJQUNsQixhQUFhLEVBQUUsTUFBTSxDQUFDO0lBQ3RCLGNBQWMsRUFBRSxjQUFjLEVBQUUsQ0FBQztJQUNqQyxPQUFPLEVBQUUsT0FBTyxDQUFDO0lBQ2pCLGtCQUFrQixDQUFDLEVBQUUsT0FBTyxDQUFDO0lBQzdCLFNBQVMsRUFBRSxNQUFNLENBQUM7SUFDbEIsV0FBVyxFQUFFLFdBQVcsQ0FBQztJQUN6QixZQUFZLEVBQUUsT0FBTyxDQUFDO0NBQ3ZCLEdBQ0EsV0FBVyxDQUFDLEVBQUUsQ0FBQyxDQTRCakI7QUFFRCx3QkFBZ0IsK0JBQStCLENBQzdDLEVBQUUsRUFBRSx3QkFBd0IsRUFDNUIsa0JBQWtCLEVBQUUsa0JBQWtCLEVBQ3RDLGVBQWUsRUFBRSxlQUFlLEVBQ2hDLGNBQWMsRUFBRSxjQUFjLEVBQUUsR0FDL0Isd0JBQXdCLENBZ0IxQiJ9
|
|
@@ -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;AAiB9D,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,CA4BjB;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"}
|