@aztec/validator-client 0.0.1-commit.ee80a48 → 0.0.1-commit.ef17749e1
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 +3 -3
- package/dest/block_proposal_handler.d.ts.map +1 -1
- package/dest/block_proposal_handler.js +63 -58
- 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.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 +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 +195 -33
- package/package.json +19 -19
- package/src/block_proposal_handler.ts +75 -76
- 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 +2 -2
- package/src/metrics.ts +18 -0
- package/src/validator.ts +243 -38
- 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
|
@@ -256,8 +256,8 @@ export class HAKeyStore implements ExtendedValidatorKeyStore {
|
|
|
256
256
|
/**
|
|
257
257
|
* Start the high-availability key store
|
|
258
258
|
*/
|
|
259
|
-
public start()
|
|
260
|
-
|
|
259
|
+
public async start() {
|
|
260
|
+
await this.haSigner.start();
|
|
261
261
|
}
|
|
262
262
|
|
|
263
263
|
/**
|
package/src/metrics.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { EpochNumber } from '@aztec/foundation/branded-types';
|
|
2
|
+
import type { EthAddress } from '@aztec/foundation/eth-address';
|
|
1
3
|
import type { BlockProposal } from '@aztec/stdlib/p2p';
|
|
2
4
|
import {
|
|
3
5
|
Attributes,
|
|
@@ -16,6 +18,8 @@ export class ValidatorMetrics {
|
|
|
16
18
|
private successfulAttestationsCount: UpDownCounter;
|
|
17
19
|
private failedAttestationsBadProposalCount: UpDownCounter;
|
|
18
20
|
private failedAttestationsNodeIssueCount: UpDownCounter;
|
|
21
|
+
private currentEpoch: Gauge;
|
|
22
|
+
private attestedEpochCount: UpDownCounter;
|
|
19
23
|
|
|
20
24
|
private reexMana: Histogram;
|
|
21
25
|
private reexTx: Histogram;
|
|
@@ -64,6 +68,10 @@ export class ValidatorMetrics {
|
|
|
64
68
|
},
|
|
65
69
|
);
|
|
66
70
|
|
|
71
|
+
this.currentEpoch = meter.createGauge(Metrics.VALIDATOR_CURRENT_EPOCH);
|
|
72
|
+
|
|
73
|
+
this.attestedEpochCount = createUpDownCounterWithDefault(meter, Metrics.VALIDATOR_ATTESTED_EPOCH_COUNT);
|
|
74
|
+
|
|
67
75
|
this.reexMana = meter.createHistogram(Metrics.VALIDATOR_RE_EXECUTION_MANA);
|
|
68
76
|
|
|
69
77
|
this.reexTx = meter.createHistogram(Metrics.VALIDATOR_RE_EXECUTION_TX_COUNT);
|
|
@@ -110,4 +118,14 @@ export class ValidatorMetrics {
|
|
|
110
118
|
[Attributes.IS_COMMITTEE_MEMBER]: inCommittee,
|
|
111
119
|
});
|
|
112
120
|
}
|
|
121
|
+
|
|
122
|
+
/** Update the gauge tracking the current epoch number (proxy for total epochs elapsed). */
|
|
123
|
+
public setCurrentEpoch(epoch: EpochNumber) {
|
|
124
|
+
this.currentEpoch.record(Number(epoch));
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** Increment the count of epochs in which the given attester submitted at least one attestation. */
|
|
128
|
+
public incAttestedEpochCount(attester: EthAddress) {
|
|
129
|
+
this.attestedEpochCount.add(1, { [Attributes.ATTESTER_ADDRESS]: attester.toString() });
|
|
130
|
+
}
|
|
113
131
|
}
|
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.warn(`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;
|
|
@@ -437,6 +488,23 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
437
488
|
return undefined;
|
|
438
489
|
}
|
|
439
490
|
|
|
491
|
+
// Ignore proposals from ourselves (may happen in HA setups)
|
|
492
|
+
if (this.getValidatorAddresses().some(addr => addr.equals(proposer))) {
|
|
493
|
+
this.log.warn(`Ignoring block proposal from self for slot ${slotNumber}`, {
|
|
494
|
+
proposer: proposer.toString(),
|
|
495
|
+
slotNumber,
|
|
496
|
+
});
|
|
497
|
+
return undefined;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
// Validate fee asset price modifier is within allowed range
|
|
501
|
+
if (!validateFeeAssetPriceModifier(proposal.feeAssetPriceModifier)) {
|
|
502
|
+
this.log.warn(
|
|
503
|
+
`Received checkpoint proposal with invalid feeAssetPriceModifier ${proposal.feeAssetPriceModifier} for slot ${slotNumber}`,
|
|
504
|
+
);
|
|
505
|
+
return undefined;
|
|
506
|
+
}
|
|
507
|
+
|
|
440
508
|
// Check that I have any address in current committee before attesting
|
|
441
509
|
const inCommittee = await this.epochCache.filterInCommittee(slotNumber, this.getValidatorAddresses());
|
|
442
510
|
const partOfCommittee = inCommittee.length > 0;
|
|
@@ -445,11 +513,9 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
445
513
|
slotNumber,
|
|
446
514
|
archive: proposal.archive.toString(),
|
|
447
515
|
proposer: proposer.toString(),
|
|
448
|
-
txCount: proposal.txHashes.length,
|
|
449
516
|
};
|
|
450
517
|
this.log.info(`Received checkpoint proposal for slot ${slotNumber}`, {
|
|
451
518
|
...proposalInfo,
|
|
452
|
-
txHashes: proposal.txHashes.map(t => t.toString()),
|
|
453
519
|
fishermanMode: this.config.fishermanMode || false,
|
|
454
520
|
});
|
|
455
521
|
|
|
@@ -485,6 +551,17 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
485
551
|
|
|
486
552
|
this.metrics.incSuccessfulAttestations(inCommittee.length);
|
|
487
553
|
|
|
554
|
+
// Track epoch participation per attester: count each (attester, epoch) pair at most once
|
|
555
|
+
const proposalEpoch = getEpochAtSlot(slotNumber, this.epochCache.getL1Constants());
|
|
556
|
+
for (const attester of inCommittee) {
|
|
557
|
+
const key = attester.toString();
|
|
558
|
+
const lastEpoch = this.lastAttestedEpochByAttester.get(key);
|
|
559
|
+
if (lastEpoch === undefined || proposalEpoch > lastEpoch) {
|
|
560
|
+
this.lastAttestedEpochByAttester.set(key, proposalEpoch);
|
|
561
|
+
this.metrics.incAttestedEpochCount(attester);
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
|
|
488
565
|
// Determine which validators should attest
|
|
489
566
|
let attestors: EthAddress[];
|
|
490
567
|
if (partOfCommittee) {
|
|
@@ -510,15 +587,45 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
510
587
|
return undefined;
|
|
511
588
|
}
|
|
512
589
|
|
|
513
|
-
return this.createCheckpointAttestationsFromProposal(proposal, attestors);
|
|
590
|
+
return await this.createCheckpointAttestationsFromProposal(proposal, attestors);
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
/**
|
|
594
|
+
* Checks if we should attest to a slot based on equivocation prevention rules.
|
|
595
|
+
* @returns true if we should attest, false if we should skip
|
|
596
|
+
*/
|
|
597
|
+
private shouldAttestToSlot(slotNumber: SlotNumber): boolean {
|
|
598
|
+
// If attestToEquivocatedProposals is true, always allow
|
|
599
|
+
if (this.config.attestToEquivocatedProposals) {
|
|
600
|
+
return true;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
// Check if incoming slot is strictly greater than last attested
|
|
604
|
+
if (this.lastAttestedProposal && slotNumber <= this.lastAttestedProposal.slotNumber) {
|
|
605
|
+
this.log.warn(
|
|
606
|
+
`Refusing to process a proposal for slot ${slotNumber} given we already attested to a proposal for slot ${this.lastAttestedProposal.slotNumber}`,
|
|
607
|
+
);
|
|
608
|
+
return false;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
return true;
|
|
514
612
|
}
|
|
515
613
|
|
|
516
614
|
private async createCheckpointAttestationsFromProposal(
|
|
517
615
|
proposal: CheckpointProposalCore,
|
|
518
616
|
attestors: EthAddress[] = [],
|
|
519
|
-
): Promise<CheckpointAttestation[]> {
|
|
617
|
+
): Promise<CheckpointAttestation[] | undefined> {
|
|
618
|
+
// Equivocation check: must happen right before signing to minimize the race window
|
|
619
|
+
if (!this.shouldAttestToSlot(proposal.slotNumber)) {
|
|
620
|
+
return undefined;
|
|
621
|
+
}
|
|
622
|
+
|
|
520
623
|
const attestations = await this.validationService.attestToCheckpointProposal(proposal, attestors);
|
|
521
|
-
|
|
624
|
+
|
|
625
|
+
// Track the proposal we attested to (to prevent equivocation)
|
|
626
|
+
this.lastAttestedProposal = proposal;
|
|
627
|
+
|
|
628
|
+
await this.p2pClient.addOwnCheckpointAttestations(attestations);
|
|
522
629
|
return attestations;
|
|
523
630
|
}
|
|
524
631
|
|
|
@@ -531,7 +638,11 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
531
638
|
proposalInfo: LogData,
|
|
532
639
|
): Promise<{ isValid: true } | { isValid: false; reason: string }> {
|
|
533
640
|
const slot = proposal.slotNumber;
|
|
534
|
-
|
|
641
|
+
|
|
642
|
+
// Timeout block syncing at the start of the next slot
|
|
643
|
+
const config = this.checkpointsBuilder.getConfig();
|
|
644
|
+
const nextSlotTimestampSeconds = Number(getTimestampForSlot(SlotNumber(slot + 1), config));
|
|
645
|
+
const timeoutSeconds = Math.max(1, nextSlotTimestampSeconds - Math.floor(this.dateProvider.now() / 1000));
|
|
535
646
|
|
|
536
647
|
// Wait for last block to sync by archive
|
|
537
648
|
let lastBlockHeader: BlockHeader | undefined;
|
|
@@ -566,6 +677,12 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
566
677
|
return { isValid: false, reason: 'no_blocks_for_slot' };
|
|
567
678
|
}
|
|
568
679
|
|
|
680
|
+
// Ensure the last block for this slot matches the archive in the checkpoint proposal
|
|
681
|
+
if (!blocks.at(-1)?.archive.root.equals(proposal.archive)) {
|
|
682
|
+
this.log.warn(`Last block archive mismatch for checkpoint proposal`, proposalInfo);
|
|
683
|
+
return { isValid: false, reason: 'last_block_archive_mismatch' };
|
|
684
|
+
}
|
|
685
|
+
|
|
569
686
|
this.log.debug(`Found ${blocks.length} blocks for slot ${slot}`, {
|
|
570
687
|
...proposalInfo,
|
|
571
688
|
blockNumbers: blocks.map(b => b.number),
|
|
@@ -579,14 +696,11 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
579
696
|
// Get L1-to-L2 messages for this checkpoint
|
|
580
697
|
const l1ToL2Messages = await this.l1ToL2MessageSource.getL1ToL2Messages(checkpointNumber);
|
|
581
698
|
|
|
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.
|
|
699
|
+
// Collect the out hashes of all the checkpoints before this one in the same epoch
|
|
585
700
|
const epoch = getEpochAtSlot(slot, this.epochCache.getL1Constants());
|
|
586
|
-
const
|
|
587
|
-
.filter(
|
|
588
|
-
.
|
|
589
|
-
const previousCheckpointOutHashes = previousCheckpoints.map(c => c.getCheckpointOutHash());
|
|
701
|
+
const previousCheckpointOutHashes = (await this.blockSource.getCheckpointsDataForEpoch(epoch))
|
|
702
|
+
.filter(c => c.checkpointNumber < checkpointNumber)
|
|
703
|
+
.map(c => c.checkpointOutHash);
|
|
590
704
|
|
|
591
705
|
// Fork world state at the block before the first block
|
|
592
706
|
const parentBlockNumber = BlockNumber(firstBlock.number - 1);
|
|
@@ -597,6 +711,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
597
711
|
const checkpointBuilder = await this.checkpointsBuilder.openCheckpoint(
|
|
598
712
|
checkpointNumber,
|
|
599
713
|
constants,
|
|
714
|
+
proposal.feeAssetPriceModifier,
|
|
600
715
|
l1ToL2Messages,
|
|
601
716
|
previousCheckpointOutHashes,
|
|
602
717
|
fork,
|
|
@@ -643,6 +758,20 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
643
758
|
return { isValid: false, reason: 'out_hash_mismatch' };
|
|
644
759
|
}
|
|
645
760
|
|
|
761
|
+
// Final round of validations on the checkpoint, just in case.
|
|
762
|
+
try {
|
|
763
|
+
validateCheckpoint(computedCheckpoint, {
|
|
764
|
+
rollupManaLimit: this.checkpointsBuilder.getConfig().rollupManaLimit,
|
|
765
|
+
maxDABlockGas: this.config.validateMaxDABlockGas,
|
|
766
|
+
maxL2BlockGas: this.config.validateMaxL2BlockGas,
|
|
767
|
+
maxTxsPerBlock: this.config.validateMaxTxsPerBlock,
|
|
768
|
+
maxTxsPerCheckpoint: this.config.validateMaxTxsPerCheckpoint,
|
|
769
|
+
});
|
|
770
|
+
} catch (err) {
|
|
771
|
+
this.log.warn(`Checkpoint validation failed: ${err}`, proposalInfo);
|
|
772
|
+
return { isValid: false, reason: 'checkpoint_validation_failed' };
|
|
773
|
+
}
|
|
774
|
+
|
|
646
775
|
this.log.verbose(`Checkpoint proposal validation successful for slot ${slot}`, proposalInfo);
|
|
647
776
|
return { isValid: true };
|
|
648
777
|
} finally {
|
|
@@ -659,6 +788,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
659
788
|
chainId: gv.chainId,
|
|
660
789
|
version: gv.version,
|
|
661
790
|
slotNumber: gv.slotNumber,
|
|
791
|
+
timestamp: gv.timestamp,
|
|
662
792
|
coinbase: gv.coinbase,
|
|
663
793
|
feeRecipient: gv.feeRecipient,
|
|
664
794
|
gasFees: gv.gasFees,
|
|
@@ -668,7 +798,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
668
798
|
/**
|
|
669
799
|
* Uploads blobs for a checkpoint to the filestore (fire and forget).
|
|
670
800
|
*/
|
|
671
|
-
|
|
801
|
+
protected async uploadBlobsForCheckpoint(proposal: CheckpointProposalCore, proposalInfo: LogData): Promise<void> {
|
|
672
802
|
try {
|
|
673
803
|
const lastBlockHeader = await this.blockSource.getBlockHeaderByArchive(proposal.archive);
|
|
674
804
|
if (!lastBlockHeader) {
|
|
@@ -683,7 +813,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
683
813
|
}
|
|
684
814
|
|
|
685
815
|
const blobFields = blocks.flatMap(b => b.toBlobFields());
|
|
686
|
-
const blobs: Blob[] = getBlobsPerL1Block(blobFields);
|
|
816
|
+
const blobs: Blob[] = await getBlobsPerL1Block(blobFields);
|
|
687
817
|
await this.blobClient.sendBlobsToFilestore(blobs);
|
|
688
818
|
this.log.debug(`Uploaded ${blobs.length} blobs to filestore for checkpoint at slot ${proposal.slotNumber}`, {
|
|
689
819
|
...proposalInfo,
|
|
@@ -721,6 +851,52 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
721
851
|
]);
|
|
722
852
|
}
|
|
723
853
|
|
|
854
|
+
/**
|
|
855
|
+
* Handle detection of a duplicate proposal (equivocation).
|
|
856
|
+
* Emits a slash event when a proposer sends multiple proposals for the same position.
|
|
857
|
+
*/
|
|
858
|
+
private handleDuplicateProposal(info: DuplicateProposalInfo): void {
|
|
859
|
+
const { slot, proposer, type } = info;
|
|
860
|
+
|
|
861
|
+
this.log.warn(`Triggering slash event for duplicate ${type} proposal from ${proposer.toString()} at slot ${slot}`, {
|
|
862
|
+
proposer: proposer.toString(),
|
|
863
|
+
slot,
|
|
864
|
+
type,
|
|
865
|
+
});
|
|
866
|
+
|
|
867
|
+
// Emit slash event
|
|
868
|
+
this.emit(WANT_TO_SLASH_EVENT, [
|
|
869
|
+
{
|
|
870
|
+
validator: proposer,
|
|
871
|
+
amount: this.config.slashDuplicateProposalPenalty,
|
|
872
|
+
offenseType: OffenseType.DUPLICATE_PROPOSAL,
|
|
873
|
+
epochOrSlot: BigInt(slot),
|
|
874
|
+
},
|
|
875
|
+
]);
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
/**
|
|
879
|
+
* Handle detection of a duplicate attestation (equivocation).
|
|
880
|
+
* Emits a slash event when an attester signs attestations for different proposals at the same slot.
|
|
881
|
+
*/
|
|
882
|
+
private handleDuplicateAttestation(info: DuplicateAttestationInfo): void {
|
|
883
|
+
const { slot, attester } = info;
|
|
884
|
+
|
|
885
|
+
this.log.warn(`Triggering slash event for duplicate attestation from ${attester.toString()} at slot ${slot}`, {
|
|
886
|
+
attester: attester.toString(),
|
|
887
|
+
slot,
|
|
888
|
+
});
|
|
889
|
+
|
|
890
|
+
this.emit(WANT_TO_SLASH_EVENT, [
|
|
891
|
+
{
|
|
892
|
+
validator: attester,
|
|
893
|
+
amount: this.config.slashDuplicateAttestationPenalty,
|
|
894
|
+
offenseType: OffenseType.DUPLICATE_ATTESTATION,
|
|
895
|
+
epochOrSlot: BigInt(slot),
|
|
896
|
+
},
|
|
897
|
+
]);
|
|
898
|
+
}
|
|
899
|
+
|
|
724
900
|
async createBlockProposal(
|
|
725
901
|
blockHeader: BlockHeader,
|
|
726
902
|
indexWithinCheckpoint: IndexWithinCheckpoint,
|
|
@@ -730,11 +906,19 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
730
906
|
proposerAddress: EthAddress | undefined,
|
|
731
907
|
options: BlockProposalOptions = {},
|
|
732
908
|
): Promise<BlockProposal> {
|
|
733
|
-
//
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
909
|
+
// Validate that we're not creating a proposal for an older or equal position
|
|
910
|
+
if (this.lastProposedBlock) {
|
|
911
|
+
const lastSlot = this.lastProposedBlock.slotNumber;
|
|
912
|
+
const lastIndex = this.lastProposedBlock.indexWithinCheckpoint;
|
|
913
|
+
const newSlot = blockHeader.globalVariables.slotNumber;
|
|
914
|
+
|
|
915
|
+
if (newSlot < lastSlot || (newSlot === lastSlot && indexWithinCheckpoint <= lastIndex)) {
|
|
916
|
+
throw new Error(
|
|
917
|
+
`Cannot create block proposal for slot ${newSlot} index ${indexWithinCheckpoint}: ` +
|
|
918
|
+
`already proposed block for slot ${lastSlot} index ${lastIndex}`,
|
|
919
|
+
);
|
|
920
|
+
}
|
|
921
|
+
}
|
|
738
922
|
|
|
739
923
|
this.log.info(
|
|
740
924
|
`Assembling block proposal for block ${blockHeader.globalVariables.blockNumber} slot ${blockHeader.globalVariables.slotNumber}`,
|
|
@@ -751,25 +935,42 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
751
935
|
broadcastInvalidBlockProposal: this.config.broadcastInvalidBlockProposal,
|
|
752
936
|
},
|
|
753
937
|
);
|
|
754
|
-
this.
|
|
938
|
+
this.lastProposedBlock = newProposal;
|
|
755
939
|
return newProposal;
|
|
756
940
|
}
|
|
757
941
|
|
|
758
942
|
async createCheckpointProposal(
|
|
759
943
|
checkpointHeader: CheckpointHeader,
|
|
760
944
|
archive: Fr,
|
|
945
|
+
feeAssetPriceModifier: bigint,
|
|
761
946
|
lastBlockInfo: CreateCheckpointProposalLastBlockData | undefined,
|
|
762
947
|
proposerAddress: EthAddress | undefined,
|
|
763
948
|
options: CheckpointProposalOptions = {},
|
|
764
949
|
): Promise<CheckpointProposal> {
|
|
950
|
+
// Validate that we're not creating a proposal for an older or equal slot
|
|
951
|
+
if (this.lastProposedCheckpoint) {
|
|
952
|
+
const lastSlot = this.lastProposedCheckpoint.slotNumber;
|
|
953
|
+
const newSlot = checkpointHeader.slotNumber;
|
|
954
|
+
|
|
955
|
+
if (newSlot <= lastSlot) {
|
|
956
|
+
throw new Error(
|
|
957
|
+
`Cannot create checkpoint proposal for slot ${newSlot}: ` +
|
|
958
|
+
`already proposed checkpoint for slot ${lastSlot}`,
|
|
959
|
+
);
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
|
|
765
963
|
this.log.info(`Assembling checkpoint proposal for slot ${checkpointHeader.slotNumber}`);
|
|
766
|
-
|
|
964
|
+
const newProposal = await this.validationService.createCheckpointProposal(
|
|
767
965
|
checkpointHeader,
|
|
768
966
|
archive,
|
|
967
|
+
feeAssetPriceModifier,
|
|
769
968
|
lastBlockInfo,
|
|
770
969
|
proposerAddress,
|
|
771
970
|
options,
|
|
772
971
|
);
|
|
972
|
+
this.lastProposedCheckpoint = newProposal;
|
|
973
|
+
return newProposal;
|
|
773
974
|
}
|
|
774
975
|
|
|
775
976
|
async broadcastBlockProposal(proposal: BlockProposal): Promise<void> {
|
|
@@ -791,6 +992,10 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
791
992
|
this.log.debug(`Collecting ${inCommittee.length} self-attestations for slot ${slot}`, { inCommittee });
|
|
792
993
|
const attestations = await this.createCheckpointAttestationsFromProposal(proposal, inCommittee);
|
|
793
994
|
|
|
995
|
+
if (!attestations) {
|
|
996
|
+
return [];
|
|
997
|
+
}
|
|
998
|
+
|
|
794
999
|
// We broadcast our own attestations to our peers so, in case our block does not get mined on L1,
|
|
795
1000
|
// other nodes can see that our validators did attest to this block proposal, and do not slash us
|
|
796
1001
|
// 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"}
|