@aztec/sequencer-client 0.67.1 → 0.68.0
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/dest/config.d.ts +3 -3
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +3 -56
- package/dest/index.d.ts +0 -1
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +1 -2
- package/dest/publisher/index.d.ts +0 -1
- package/dest/publisher/index.d.ts.map +1 -1
- package/dest/publisher/index.js +1 -2
- package/dest/publisher/l1-publisher-metrics.d.ts +5 -2
- package/dest/publisher/l1-publisher-metrics.d.ts.map +1 -1
- package/dest/publisher/l1-publisher-metrics.js +16 -1
- package/dest/publisher/l1-publisher.d.ts +4 -1
- package/dest/publisher/l1-publisher.d.ts.map +1 -1
- package/dest/publisher/l1-publisher.js +200 -44
- package/dest/publisher/utils.d.ts +1 -3
- package/dest/publisher/utils.d.ts.map +1 -1
- package/dest/publisher/utils.js +2 -8
- package/dest/sequencer/allowed.d.ts +3 -0
- package/dest/sequencer/allowed.d.ts.map +1 -0
- package/dest/sequencer/allowed.js +34 -0
- package/dest/sequencer/config.d.ts +1 -1
- package/dest/sequencer/config.d.ts.map +1 -1
- package/dest/sequencer/metrics.d.ts +2 -0
- package/dest/sequencer/metrics.d.ts.map +1 -1
- package/dest/sequencer/metrics.js +13 -2
- package/dest/sequencer/sequencer.d.ts +1 -4
- package/dest/sequencer/sequencer.d.ts.map +1 -1
- package/dest/sequencer/sequencer.js +51 -54
- package/dest/tx_validator/gas_validator.d.ts +3 -4
- package/dest/tx_validator/gas_validator.d.ts.map +1 -1
- package/dest/tx_validator/gas_validator.js +25 -8
- package/dest/tx_validator/tx_validator_factory.d.ts.map +1 -1
- package/dest/tx_validator/tx_validator_factory.js +6 -4
- package/package.json +26 -23
- package/src/config.ts +5 -60
- package/src/index.ts +0 -1
- package/src/publisher/index.ts +0 -1
- package/src/publisher/l1-publisher-metrics.ts +24 -3
- package/src/publisher/l1-publisher.ts +241 -68
- package/src/publisher/utils.ts +1 -10
- package/src/sequencer/allowed.ts +36 -0
- package/src/sequencer/config.ts +1 -1
- package/src/sequencer/metrics.ts +15 -1
- package/src/sequencer/sequencer.ts +61 -70
- package/src/tx_validator/gas_validator.ts +32 -6
- package/src/tx_validator/tx_validator_factory.ts +11 -3
|
@@ -38,6 +38,7 @@ import { type GlobalVariableBuilder } from '../global_variable_builder/global_bu
|
|
|
38
38
|
import { type L1Publisher } from '../publisher/l1-publisher.js';
|
|
39
39
|
import { prettyLogViemErrorMsg } from '../publisher/utils.js';
|
|
40
40
|
import { type TxValidatorFactory } from '../tx_validator/tx_validator_factory.js';
|
|
41
|
+
import { getDefaultAllowedSetupFunctions } from './allowed.js';
|
|
41
42
|
import { type SequencerConfig } from './config.js';
|
|
42
43
|
import { SequencerMetrics } from './metrics.js';
|
|
43
44
|
import { SequencerState, getSecondsIntoSlot, orderAttestations } from './utils.js';
|
|
@@ -76,13 +77,11 @@ export class Sequencer {
|
|
|
76
77
|
private pollingIntervalMs: number = 1000;
|
|
77
78
|
private maxTxsPerBlock = 32;
|
|
78
79
|
private minTxsPerBLock = 1;
|
|
79
|
-
private minSecondsBetweenBlocks = 0;
|
|
80
|
-
private maxSecondsBetweenBlocks = 0;
|
|
81
80
|
// TODO: zero values should not be allowed for the following 2 values in PROD
|
|
82
81
|
private _coinbase = EthAddress.ZERO;
|
|
83
82
|
private _feeRecipient = AztecAddress.ZERO;
|
|
84
83
|
private state = SequencerState.STOPPED;
|
|
85
|
-
private allowedInSetup: AllowedElement[] =
|
|
84
|
+
private allowedInSetup: AllowedElement[] = getDefaultAllowedSetupFunctions();
|
|
86
85
|
private maxBlockSizeInBytes: number = 1024 * 1024;
|
|
87
86
|
private metrics: SequencerMetrics;
|
|
88
87
|
private isFlushing: boolean = false;
|
|
@@ -127,10 +126,7 @@ export class Sequencer {
|
|
|
127
126
|
* @param config - New parameters.
|
|
128
127
|
*/
|
|
129
128
|
public updateConfig(config: SequencerConfig) {
|
|
130
|
-
this.log.info(
|
|
131
|
-
`Sequencer config set`,
|
|
132
|
-
omit(pickFromSchema(this.config, SequencerConfigSchema), 'allowedInSetup', 'allowedInTeardown'),
|
|
133
|
-
);
|
|
129
|
+
this.log.info(`Sequencer config set`, omit(pickFromSchema(config, SequencerConfigSchema), 'allowedInSetup'));
|
|
134
130
|
|
|
135
131
|
if (config.transactionPollingIntervalMS !== undefined) {
|
|
136
132
|
this.pollingIntervalMs = config.transactionPollingIntervalMS;
|
|
@@ -141,12 +137,6 @@ export class Sequencer {
|
|
|
141
137
|
if (config.minTxsPerBlock !== undefined) {
|
|
142
138
|
this.minTxsPerBLock = config.minTxsPerBlock;
|
|
143
139
|
}
|
|
144
|
-
if (config.minSecondsBetweenBlocks !== undefined) {
|
|
145
|
-
this.minSecondsBetweenBlocks = config.minSecondsBetweenBlocks;
|
|
146
|
-
}
|
|
147
|
-
if (config.maxSecondsBetweenBlocks !== undefined) {
|
|
148
|
-
this.maxSecondsBetweenBlocks = config.maxSecondsBetweenBlocks;
|
|
149
|
-
}
|
|
150
140
|
if (config.coinbase) {
|
|
151
141
|
this._coinbase = config.coinbase;
|
|
152
142
|
}
|
|
@@ -192,7 +182,7 @@ export class Sequencer {
|
|
|
192
182
|
* Starts the sequencer and moves to IDLE state.
|
|
193
183
|
*/
|
|
194
184
|
public start() {
|
|
195
|
-
this.runningPromise = new RunningPromise(this.work.bind(this), this.pollingIntervalMs);
|
|
185
|
+
this.runningPromise = new RunningPromise(this.work.bind(this), this.log, this.pollingIntervalMs);
|
|
196
186
|
this.setState(SequencerState.IDLE, 0n, true /** force */);
|
|
197
187
|
this.runningPromise.start();
|
|
198
188
|
this.log.info(`Sequencer started with address ${this.publisher.getSenderAddress().toString()}`);
|
|
@@ -293,6 +283,7 @@ export class Sequencer {
|
|
|
293
283
|
const pendingTxs = await this.p2pClient.getPendingTxs();
|
|
294
284
|
|
|
295
285
|
if (!this.shouldProposeBlock(historicalHeader, { pendingTxsCount: pendingTxs.length })) {
|
|
286
|
+
await this.claimEpochProofRightIfAvailable(slot);
|
|
296
287
|
return;
|
|
297
288
|
}
|
|
298
289
|
|
|
@@ -325,6 +316,7 @@ export class Sequencer {
|
|
|
325
316
|
|
|
326
317
|
// Bail if we don't have enough valid txs
|
|
327
318
|
if (!this.shouldProposeBlock(historicalHeader, { validTxsCount: validTxs.length })) {
|
|
319
|
+
await this.claimEpochProofRightIfAvailable(slot);
|
|
328
320
|
return;
|
|
329
321
|
}
|
|
330
322
|
|
|
@@ -339,6 +331,7 @@ export class Sequencer {
|
|
|
339
331
|
this.setState(SequencerState.IDLE, 0n);
|
|
340
332
|
}
|
|
341
333
|
|
|
334
|
+
@trackSpan('Sequencer.work')
|
|
342
335
|
protected async work() {
|
|
343
336
|
try {
|
|
344
337
|
await this.doRealWork();
|
|
@@ -354,15 +347,6 @@ export class Sequencer {
|
|
|
354
347
|
}
|
|
355
348
|
}
|
|
356
349
|
|
|
357
|
-
/** Whether to skip the check of min txs per block if more than maxSecondsBetweenBlocks has passed since the previous block. */
|
|
358
|
-
private skipMinTxsPerBlockCheck(historicalHeader: BlockHeader | undefined): boolean {
|
|
359
|
-
const lastBlockTime = historicalHeader?.globalVariables.timestamp.toNumber() || 0;
|
|
360
|
-
const currentTime = Math.floor(Date.now() / 1000);
|
|
361
|
-
const elapsed = currentTime - lastBlockTime;
|
|
362
|
-
|
|
363
|
-
return this.maxSecondsBetweenBlocks > 0 && elapsed >= this.maxSecondsBetweenBlocks;
|
|
364
|
-
}
|
|
365
|
-
|
|
366
350
|
async mayProposeBlock(tipArchive: Buffer, proposalBlockNumber: bigint): Promise<bigint> {
|
|
367
351
|
// This checks that we can propose, and gives us the slot that we are to propose for
|
|
368
352
|
try {
|
|
@@ -445,53 +429,29 @@ export class Sequencer {
|
|
|
445
429
|
`Last block mined at ${lastBlockTime} current time is ${currentTime} (elapsed ${elapsedSinceLastBlock})`,
|
|
446
430
|
);
|
|
447
431
|
|
|
448
|
-
//
|
|
449
|
-
|
|
450
|
-
if (this.minSecondsBetweenBlocks > 0 && elapsedSinceLastBlock < this.minSecondsBetweenBlocks) {
|
|
432
|
+
// We need to have at least minTxsPerBLock txs.
|
|
433
|
+
if (args.pendingTxsCount !== undefined && args.pendingTxsCount < this.minTxsPerBLock) {
|
|
451
434
|
this.log.verbose(
|
|
452
|
-
`Not creating block because not enough
|
|
435
|
+
`Not creating block because not enough txs in the pool (got ${args.pendingTxsCount} min ${this.minTxsPerBLock})`,
|
|
453
436
|
);
|
|
454
437
|
return false;
|
|
455
438
|
}
|
|
456
439
|
|
|
457
|
-
const skipCheck = this.skipMinTxsPerBlockCheck(historicalHeader);
|
|
458
|
-
|
|
459
|
-
// If we haven't hit the maxSecondsBetweenBlocks, we need to have at least minTxsPerBLock txs.
|
|
460
|
-
if (args.pendingTxsCount != undefined) {
|
|
461
|
-
if (args.pendingTxsCount < this.minTxsPerBLock) {
|
|
462
|
-
if (skipCheck) {
|
|
463
|
-
this.log.debug(
|
|
464
|
-
`Creating block with only ${args.pendingTxsCount} txs as more than ${this.maxSecondsBetweenBlocks}s have passed since last block`,
|
|
465
|
-
);
|
|
466
|
-
} else {
|
|
467
|
-
this.log.verbose(
|
|
468
|
-
`Not creating block because not enough txs in the pool (got ${args.pendingTxsCount} min ${this.minTxsPerBLock})`,
|
|
469
|
-
);
|
|
470
|
-
return false;
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
|
|
475
440
|
// Bail if we don't have enough valid txs
|
|
476
|
-
if (args.validTxsCount
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
);
|
|
482
|
-
return false;
|
|
483
|
-
}
|
|
441
|
+
if (args.validTxsCount !== undefined && args.validTxsCount < this.minTxsPerBLock) {
|
|
442
|
+
this.log.verbose(
|
|
443
|
+
`Not creating block because not enough valid txs loaded from the pool (got ${args.validTxsCount} min ${this.minTxsPerBLock})`,
|
|
444
|
+
);
|
|
445
|
+
return false;
|
|
484
446
|
}
|
|
485
447
|
|
|
486
448
|
// TODO: This check should be processedTxs.length < this.minTxsPerBLock, so we don't publish a block with
|
|
487
449
|
// less txs than the minimum. But that'd cause the entire block to be aborted and retried. Instead, we should
|
|
488
450
|
// go back to the p2p pool and load more txs until we hit our minTxsPerBLock target. Only if there are no txs
|
|
489
451
|
// we should bail.
|
|
490
|
-
if (args.processedTxsCount
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
return false;
|
|
494
|
-
}
|
|
452
|
+
if (args.processedTxsCount === 0 && this.minTxsPerBLock > 0) {
|
|
453
|
+
this.log.verbose('No txs processed correctly to build block.');
|
|
454
|
+
return false;
|
|
495
455
|
}
|
|
496
456
|
|
|
497
457
|
return true;
|
|
@@ -541,18 +501,18 @@ export class Sequencer {
|
|
|
541
501
|
const orchestratorFork = await this.worldState.fork();
|
|
542
502
|
|
|
543
503
|
try {
|
|
544
|
-
const processor = this.publicProcessorFactory.create(
|
|
504
|
+
const processor = this.publicProcessorFactory.create(
|
|
505
|
+
publicProcessorFork,
|
|
506
|
+
historicalHeader,
|
|
507
|
+
newGlobalVariables,
|
|
508
|
+
true,
|
|
509
|
+
);
|
|
545
510
|
const blockBuildingTimer = new Timer();
|
|
546
511
|
const blockBuilder = this.blockBuilderFactory.create(orchestratorFork);
|
|
547
|
-
await blockBuilder.startNewBlock(
|
|
512
|
+
await blockBuilder.startNewBlock(newGlobalVariables, l1ToL2Messages);
|
|
548
513
|
|
|
549
514
|
const [publicProcessorDuration, [processedTxs, failedTxs]] = await elapsed(() =>
|
|
550
|
-
processor.process(
|
|
551
|
-
validTxs,
|
|
552
|
-
blockSize,
|
|
553
|
-
blockBuilder,
|
|
554
|
-
this.txValidatorFactory.validatorForProcessedTxs(publicProcessorFork),
|
|
555
|
-
),
|
|
515
|
+
processor.process(validTxs, blockSize, this.txValidatorFactory.validatorForProcessedTxs(publicProcessorFork)),
|
|
556
516
|
);
|
|
557
517
|
if (failedTxs.length > 0) {
|
|
558
518
|
const failedTxData = failedTxs.map(fail => fail.tx);
|
|
@@ -560,6 +520,12 @@ export class Sequencer {
|
|
|
560
520
|
await this.p2pClient.deleteTxs(Tx.getHashes(failedTxData));
|
|
561
521
|
}
|
|
562
522
|
|
|
523
|
+
const start = process.hrtime.bigint();
|
|
524
|
+
await blockBuilder.addTxs(processedTxs);
|
|
525
|
+
const end = process.hrtime.bigint();
|
|
526
|
+
const duration = Number(end - start) / 1_000;
|
|
527
|
+
this.metrics.recordBlockBuilderTreeInsertions(duration);
|
|
528
|
+
|
|
563
529
|
await interrupt?.(processedTxs);
|
|
564
530
|
|
|
565
531
|
// All real transactions have been added, set the block as full and complete the proving.
|
|
@@ -649,7 +615,6 @@ export class Sequencer {
|
|
|
649
615
|
const blockHash = block.hash();
|
|
650
616
|
const txHashes = validTxs.map(tx => tx.getTxHash());
|
|
651
617
|
this.log.info(`Built block ${block.number} with hash ${blockHash}`, {
|
|
652
|
-
txEffectsHash: block.header.contentCommitment.txsEffectsHash.toString('hex'),
|
|
653
618
|
blockHash,
|
|
654
619
|
globalVariables: block.header.globalVariables.toInspect(),
|
|
655
620
|
txHashes,
|
|
@@ -748,7 +713,7 @@ export class Sequencer {
|
|
|
748
713
|
// Find out which epoch we are currently in
|
|
749
714
|
const epochToProve = await this.publisher.getClaimableEpoch();
|
|
750
715
|
if (epochToProve === undefined) {
|
|
751
|
-
this.log.
|
|
716
|
+
this.log.trace(`No epoch to prove at slot ${slotNumber}`);
|
|
752
717
|
return undefined;
|
|
753
718
|
}
|
|
754
719
|
|
|
@@ -761,7 +726,10 @@ export class Sequencer {
|
|
|
761
726
|
});
|
|
762
727
|
// ensure these quotes are still valid for the slot and have the contract validate them
|
|
763
728
|
const validQuotesPromise = Promise.all(
|
|
764
|
-
quotes
|
|
729
|
+
quotes
|
|
730
|
+
.filter(x => x.payload.validUntilSlot >= slotNumber)
|
|
731
|
+
.filter(x => x.payload.epochToProve === epochToProve)
|
|
732
|
+
.map(x => this.publisher.validateProofQuote(x)),
|
|
765
733
|
);
|
|
766
734
|
|
|
767
735
|
const validQuotes = (await validQuotesPromise).filter((q): q is EpochProofQuote => !!q);
|
|
@@ -774,7 +742,7 @@ export class Sequencer {
|
|
|
774
742
|
(a: EpochProofQuote, b: EpochProofQuote) => a.payload.basisPointFee - b.payload.basisPointFee,
|
|
775
743
|
);
|
|
776
744
|
const quote = sortedQuotes[0];
|
|
777
|
-
this.log.info(`Selected proof quote for proof claim`, quote.
|
|
745
|
+
this.log.info(`Selected proof quote for proof claim`, { quote: quote.toInspect() });
|
|
778
746
|
return quote;
|
|
779
747
|
} catch (err) {
|
|
780
748
|
this.log.error(`Failed to create proof claim for previous epoch`, err, { slotNumber });
|
|
@@ -834,6 +802,29 @@ export class Sequencer {
|
|
|
834
802
|
return toReturn;
|
|
835
803
|
}
|
|
836
804
|
|
|
805
|
+
@trackSpan(
|
|
806
|
+
'Sequencer.claimEpochProofRightIfAvailable',
|
|
807
|
+
slotNumber => ({ [Attributes.SLOT_NUMBER]: Number(slotNumber) }),
|
|
808
|
+
epoch => ({ [Attributes.EPOCH_NUMBER]: Number(epoch) }),
|
|
809
|
+
)
|
|
810
|
+
/** Collects an epoch proof quote if there is an epoch to prove, and submits it to the L1 contract. */
|
|
811
|
+
protected async claimEpochProofRightIfAvailable(slotNumber: bigint) {
|
|
812
|
+
const proofQuote = await this.createProofClaimForPreviousEpoch(slotNumber);
|
|
813
|
+
if (proofQuote === undefined) {
|
|
814
|
+
return;
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
const epoch = proofQuote.payload.epochToProve;
|
|
818
|
+
const ctx = { slotNumber, epoch, quote: proofQuote.toInspect() };
|
|
819
|
+
this.log.verbose(`Claiming proof right for epoch ${epoch}`, ctx);
|
|
820
|
+
const success = await this.publisher.claimEpochProofRight(proofQuote);
|
|
821
|
+
if (!success) {
|
|
822
|
+
throw new Error(`Failed to claim proof right for epoch ${epoch}`);
|
|
823
|
+
}
|
|
824
|
+
this.log.info(`Claimed proof right for epoch ${epoch}`, ctx);
|
|
825
|
+
return epoch;
|
|
826
|
+
}
|
|
827
|
+
|
|
837
828
|
/**
|
|
838
829
|
* Returns whether all dependencies have caught up.
|
|
839
830
|
* We don't check against the previous block submitted since it may have been reorg'd out.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type Tx, TxExecutionPhase, type TxValidator } from '@aztec/circuit-types';
|
|
2
|
-
import { type AztecAddress, type Fr, FunctionSelector } from '@aztec/circuits.js';
|
|
2
|
+
import { type AztecAddress, type Fr, FunctionSelector, type GasFees } from '@aztec/circuits.js';
|
|
3
3
|
import { createLogger } from '@aztec/foundation/log';
|
|
4
4
|
import { computeFeePayerBalanceStorageSlot, getExecutionRequestsByPhase } from '@aztec/simulator';
|
|
5
5
|
|
|
@@ -12,36 +12,62 @@ export class GasTxValidator implements TxValidator<Tx> {
|
|
|
12
12
|
#log = createLogger('sequencer:tx_validator:tx_gas');
|
|
13
13
|
#publicDataSource: PublicStateSource;
|
|
14
14
|
#feeJuiceAddress: AztecAddress;
|
|
15
|
+
#enforceFees: boolean;
|
|
16
|
+
#gasFees: GasFees;
|
|
15
17
|
|
|
16
|
-
constructor(
|
|
18
|
+
constructor(
|
|
19
|
+
publicDataSource: PublicStateSource,
|
|
20
|
+
feeJuiceAddress: AztecAddress,
|
|
21
|
+
enforceFees: boolean,
|
|
22
|
+
gasFees: GasFees,
|
|
23
|
+
) {
|
|
17
24
|
this.#publicDataSource = publicDataSource;
|
|
18
25
|
this.#feeJuiceAddress = feeJuiceAddress;
|
|
26
|
+
this.#enforceFees = enforceFees;
|
|
27
|
+
this.#gasFees = gasFees;
|
|
19
28
|
}
|
|
20
29
|
|
|
21
|
-
async validateTxs(txs: Tx[]): Promise<[validTxs: Tx[], invalidTxs: Tx[]]> {
|
|
30
|
+
async validateTxs(txs: Tx[]): Promise<[validTxs: Tx[], invalidTxs: Tx[], skippedTxs: Tx[]]> {
|
|
22
31
|
const validTxs: Tx[] = [];
|
|
23
32
|
const invalidTxs: Tx[] = [];
|
|
33
|
+
const skippedTxs: Tx[] = [];
|
|
24
34
|
|
|
25
35
|
for (const tx of txs) {
|
|
26
|
-
if (
|
|
36
|
+
if (this.#shouldSkip(tx)) {
|
|
37
|
+
skippedTxs.push(tx);
|
|
38
|
+
} else if (await this.#validateTxFee(tx)) {
|
|
27
39
|
validTxs.push(tx);
|
|
28
40
|
} else {
|
|
29
41
|
invalidTxs.push(tx);
|
|
30
42
|
}
|
|
31
43
|
}
|
|
32
44
|
|
|
33
|
-
return [validTxs, invalidTxs];
|
|
45
|
+
return [validTxs, invalidTxs, skippedTxs];
|
|
34
46
|
}
|
|
35
47
|
|
|
36
48
|
validateTx(tx: Tx): Promise<boolean> {
|
|
37
49
|
return this.#validateTxFee(tx);
|
|
38
50
|
}
|
|
39
51
|
|
|
52
|
+
#shouldSkip(tx: Tx): boolean {
|
|
53
|
+
const gasSettings = tx.data.constants.txContext.gasSettings;
|
|
54
|
+
|
|
55
|
+
// Skip the tx if its max fees are not enough for the current block's gas fees.
|
|
56
|
+
const maxFeesPerGas = gasSettings.maxFeesPerGas;
|
|
57
|
+
const notEnoughMaxFees =
|
|
58
|
+
maxFeesPerGas.feePerDaGas.lt(this.#gasFees.feePerDaGas) ||
|
|
59
|
+
maxFeesPerGas.feePerL2Gas.lt(this.#gasFees.feePerL2Gas);
|
|
60
|
+
if (notEnoughMaxFees) {
|
|
61
|
+
this.#log.warn(`Skipping transaction ${tx.getTxHash()} due to insufficient fee per gas`);
|
|
62
|
+
}
|
|
63
|
+
return notEnoughMaxFees;
|
|
64
|
+
}
|
|
65
|
+
|
|
40
66
|
async #validateTxFee(tx: Tx): Promise<boolean> {
|
|
41
67
|
const feePayer = tx.data.feePayer;
|
|
42
68
|
// TODO(@spalladino) Eventually remove the is_zero condition as we should always charge fees to every tx
|
|
43
69
|
if (feePayer.isZero()) {
|
|
44
|
-
if (this
|
|
70
|
+
if (this.#enforceFees) {
|
|
45
71
|
this.#log.warn(`Rejecting transaction ${tx.getTxHash()} due to missing fee payer`);
|
|
46
72
|
} else {
|
|
47
73
|
return true;
|
|
@@ -29,7 +29,10 @@ export class TxValidatorFactory {
|
|
|
29
29
|
private enforceFees: boolean,
|
|
30
30
|
) {
|
|
31
31
|
this.nullifierSource = {
|
|
32
|
-
|
|
32
|
+
getNullifierIndices: nullifiers =>
|
|
33
|
+
this.committedDb
|
|
34
|
+
.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, nullifiers)
|
|
35
|
+
.then(x => x.filter(index => index !== undefined) as bigint[]),
|
|
33
36
|
};
|
|
34
37
|
|
|
35
38
|
this.publicStateSource = {
|
|
@@ -45,13 +48,18 @@ export class TxValidatorFactory {
|
|
|
45
48
|
new MetadataTxValidator(globalVariables.chainId, globalVariables.blockNumber),
|
|
46
49
|
new DoubleSpendTxValidator(this.nullifierSource),
|
|
47
50
|
new PhasesTxValidator(this.contractDataSource, setupAllowList),
|
|
48
|
-
new GasTxValidator(
|
|
51
|
+
new GasTxValidator(
|
|
52
|
+
this.publicStateSource,
|
|
53
|
+
ProtocolContractAddress.FeeJuice,
|
|
54
|
+
this.enforceFees,
|
|
55
|
+
globalVariables.gasFees,
|
|
56
|
+
),
|
|
49
57
|
);
|
|
50
58
|
}
|
|
51
59
|
|
|
52
60
|
validatorForProcessedTxs(fork: MerkleTreeReadOperations): TxValidator<ProcessedTx> {
|
|
53
61
|
return new DoubleSpendTxValidator({
|
|
54
|
-
|
|
62
|
+
getNullifierIndices: nullifiers => fork.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, nullifiers),
|
|
55
63
|
});
|
|
56
64
|
}
|
|
57
65
|
}
|