@aztec/sequencer-client 0.55.1 → 0.57.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.
Files changed (53) hide show
  1. package/dest/block_builder/index.d.ts +5 -24
  2. package/dest/block_builder/index.d.ts.map +1 -1
  3. package/dest/block_builder/index.js +3 -40
  4. package/dest/block_builder/light.d.ts +26 -0
  5. package/dest/block_builder/light.d.ts.map +1 -0
  6. package/dest/block_builder/light.js +60 -0
  7. package/dest/block_builder/orchestrator.d.ts +23 -0
  8. package/dest/block_builder/orchestrator.d.ts.map +1 -0
  9. package/dest/block_builder/orchestrator.js +33 -0
  10. package/dest/client/sequencer-client.d.ts +2 -1
  11. package/dest/client/sequencer-client.d.ts.map +1 -1
  12. package/dest/client/sequencer-client.js +3 -3
  13. package/dest/config.js +8 -5
  14. package/dest/global_variable_builder/global_builder.d.ts.map +1 -1
  15. package/dest/global_variable_builder/global_builder.js +2 -1
  16. package/dest/publisher/config.d.ts.map +1 -1
  17. package/dest/publisher/config.js +7 -2
  18. package/dest/publisher/index.d.ts +1 -1
  19. package/dest/publisher/index.d.ts.map +1 -1
  20. package/dest/publisher/index.js +1 -1
  21. package/dest/publisher/l1-publisher.d.ts +46 -34
  22. package/dest/publisher/l1-publisher.d.ts.map +1 -1
  23. package/dest/publisher/l1-publisher.js +259 -116
  24. package/dest/publisher/utils.d.ts +1 -1
  25. package/dest/publisher/utils.js +1 -1
  26. package/dest/sequencer/sequencer.d.ts +7 -3
  27. package/dest/sequencer/sequencer.d.ts.map +1 -1
  28. package/dest/sequencer/sequencer.js +62 -23
  29. package/dest/tx_validator/gas_validator.d.ts +1 -0
  30. package/dest/tx_validator/gas_validator.d.ts.map +1 -1
  31. package/dest/tx_validator/gas_validator.js +13 -8
  32. package/dest/tx_validator/phases_validator.d.ts +1 -0
  33. package/dest/tx_validator/phases_validator.d.ts.map +1 -1
  34. package/dest/tx_validator/phases_validator.js +20 -20
  35. package/dest/tx_validator/test_utils.js +4 -4
  36. package/dest/tx_validator/tx_validator_factory.d.ts.map +1 -1
  37. package/dest/tx_validator/tx_validator_factory.js +5 -4
  38. package/package.json +22 -19
  39. package/src/block_builder/index.ts +5 -49
  40. package/src/block_builder/light.ts +102 -0
  41. package/src/block_builder/orchestrator.ts +38 -0
  42. package/src/client/sequencer-client.ts +4 -3
  43. package/src/config.ts +7 -4
  44. package/src/global_variable_builder/global_builder.ts +1 -0
  45. package/src/publisher/config.ts +6 -1
  46. package/src/publisher/index.ts +1 -1
  47. package/src/publisher/l1-publisher.ts +373 -144
  48. package/src/publisher/utils.ts +1 -1
  49. package/src/sequencer/sequencer.ts +74 -26
  50. package/src/tx_validator/gas_validator.ts +13 -10
  51. package/src/tx_validator/phases_validator.ts +5 -6
  52. package/src/tx_validator/test_utils.ts +3 -3
  53. package/src/tx_validator/tx_validator_factory.ts +5 -4
@@ -1,27 +1,28 @@
1
1
  import {
2
2
  type BlockAttestation,
3
+ type EpochProofQuote,
3
4
  type L1ToL2MessageSource,
4
5
  type L2Block,
5
6
  type L2BlockSource,
6
7
  type ProcessedTx,
7
- Signature,
8
8
  Tx,
9
9
  type TxHash,
10
10
  type TxValidator,
11
11
  type WorldStateStatus,
12
12
  type WorldStateSynchronizer,
13
13
  } from '@aztec/circuit-types';
14
- import { type AllowedElement, BlockProofError, PROVING_STATUS } from '@aztec/circuit-types/interfaces';
14
+ import { type AllowedElement, BlockProofError } from '@aztec/circuit-types/interfaces';
15
15
  import { type L2BlockBuiltStats } from '@aztec/circuit-types/stats';
16
16
  import {
17
17
  AppendOnlyTreeSnapshot,
18
- AztecAddress,
19
18
  ContentCommitment,
20
- EthAddress,
21
19
  GENESIS_ARCHIVE_ROOT,
22
20
  Header,
23
21
  StateReference,
24
22
  } from '@aztec/circuits.js';
23
+ import { AztecAddress } from '@aztec/foundation/aztec-address';
24
+ import { EthAddress } from '@aztec/foundation/eth-address';
25
+ import { Signature } from '@aztec/foundation/eth-signature';
25
26
  import { Fr } from '@aztec/foundation/fields';
26
27
  import { createDebugLogger } from '@aztec/foundation/log';
27
28
  import { RunningPromise } from '@aztec/foundation/running-promise';
@@ -31,10 +32,12 @@ import { type PublicProcessorFactory } from '@aztec/simulator';
31
32
  import { Attributes, type TelemetryClient, type Tracer, trackSpan } from '@aztec/telemetry-client';
32
33
  import { type ValidatorClient } from '@aztec/validator-client';
33
34
 
35
+ import { inspect } from 'util';
36
+
34
37
  import { type BlockBuilderFactory } from '../block_builder/index.js';
35
38
  import { type GlobalVariableBuilder } from '../global_variable_builder/global_builder.js';
36
39
  import { type L1Publisher } from '../publisher/l1-publisher.js';
37
- import { prettyLogVeimError } from '../publisher/utils.js';
40
+ import { prettyLogViemError } from '../publisher/utils.js';
38
41
  import { type TxValidatorFactory } from '../tx_validator/tx_validator_factory.js';
39
42
  import { type SequencerConfig } from './config.js';
40
43
  import { SequencerMetrics } from './metrics.js';
@@ -311,7 +314,7 @@ export class Sequencer {
311
314
  this.log.debug(`Can propose block ${proposalBlockNumber} at slot ${slot}`);
312
315
  return slot;
313
316
  } catch (err) {
314
- prettyLogVeimError(err, this.log);
317
+ prettyLogViemError(err, this.log);
315
318
  throw err;
316
319
  }
317
320
  }
@@ -424,7 +427,7 @@ export class Sequencer {
424
427
 
425
428
  const blockBuildingTimer = new Timer();
426
429
  const blockBuilder = this.blockBuilderFactory.create(this.worldState.getLatest());
427
- const blockTicket = await blockBuilder.startNewBlock(blockSize, newGlobalVariables, l1ToL2Messages);
430
+ await blockBuilder.startNewBlock(blockSize, newGlobalVariables, l1ToL2Messages);
428
431
 
429
432
  const [publicProcessorDuration, [processedTxs, failedTxs]] = await elapsed(() =>
430
433
  processor.process(validTxs, blockSize, blockBuilder, this.txValidatorFactory.validatorForProcessedTxs()),
@@ -443,23 +446,15 @@ export class Sequencer {
443
446
  processedTxsCount: processedTxs.length,
444
447
  })
445
448
  ) {
446
- blockBuilder.cancelBlock();
449
+ // TODO: Roll back changes to world state
447
450
  throw new Error('Should not propose the block');
448
451
  }
449
452
 
450
453
  // All real transactions have been added, set the block as full and complete the proving.
451
- await blockBuilder.setBlockCompleted();
454
+ const block = await blockBuilder.setBlockCompleted();
452
455
 
453
- // Here we are now waiting for the block to be proven (using simulated[fake] proofs).
454
456
  // TODO(@PhilWindle) We should probably periodically check for things like another
455
457
  // block being published before ours instead of just waiting on our block
456
- const result = await blockTicket.provingPromise;
457
- if (result.status === PROVING_STATUS.FAILURE) {
458
- throw new Error(`Block proving failed, reason: ${result.reason}`);
459
- }
460
-
461
- // Block is ready, now finalise
462
- const { block } = await blockBuilder.finaliseBlock();
463
458
 
464
459
  await this.publisher.validateBlockForSubmission(block.header);
465
460
 
@@ -488,13 +483,17 @@ export class Sequencer {
488
483
  const attestations = await this.collectAttestations(block, txHashes);
489
484
  this.log.verbose('Attestations collected');
490
485
 
486
+ this.log.verbose('Collecting proof quotes');
487
+ const proofQuote = await this.createProofClaimForPreviousEpoch(newGlobalVariables.slotNumber.toBigInt());
488
+ this.log.verbose(proofQuote ? `Using proof quote ${inspect(proofQuote.payload)}` : 'No proof quote available');
489
+
491
490
  try {
492
- await this.publishL2Block(block, attestations, txHashes);
491
+ await this.publishL2Block(block, attestations, txHashes, proofQuote);
493
492
  this.metrics.recordPublishedBlock(workDuration);
494
493
  this.log.info(
495
- `Submitted rollup block ${block.number} with ${
496
- processedTxs.length
497
- } transactions duration=${workDuration}ms (Submitter: ${await this.publisher.getSenderAddress()})`,
494
+ `Submitted rollup block ${block.number} with ${processedTxs.length} transactions duration=${Math.ceil(
495
+ workDuration,
496
+ )}ms (Submitter: ${this.publisher.getSenderAddress()})`,
498
497
  );
499
498
  } catch (err) {
500
499
  this.metrics.recordFailedBlock();
@@ -537,7 +536,51 @@ export class Sequencer {
537
536
  this.log.verbose(`Collected attestations from validators, number of attestations: ${attestations.length}`);
538
537
 
539
538
  // note: the smart contract requires that the signatures are provided in the order of the committee
540
- return await orderAttestations(attestations, committee);
539
+ return orderAttestations(attestations, committee);
540
+ }
541
+
542
+ protected async createProofClaimForPreviousEpoch(slotNumber: bigint): Promise<EpochProofQuote | undefined> {
543
+ try {
544
+ // Find out which epoch we are currently in
545
+ const epochForBlock = await this.publisher.getEpochForSlotNumber(slotNumber);
546
+ if (epochForBlock < 1n) {
547
+ // It's the 0th epoch, nothing to be proven yet
548
+ this.log.verbose(`First epoch has no claim`);
549
+ return undefined;
550
+ }
551
+ const epochToProve = epochForBlock - 1n;
552
+ // Find out the next epoch that can be claimed
553
+ const canClaim = await this.publisher.nextEpochToClaim();
554
+ if (canClaim != epochToProve) {
555
+ // It's not the one we are looking to claim
556
+ this.log.verbose(`Unable to claim previous epoch (${canClaim} != ${epochToProve})`);
557
+ return undefined;
558
+ }
559
+ // Get quotes for the epoch to be proven
560
+ const quotes = await this.p2pClient.getEpochProofQuotes(epochToProve);
561
+ this.log.verbose(`Retrieved ${quotes.length} quotes, slot: ${slotNumber}, epoch to prove: ${epochToProve}`);
562
+ for (const quote of quotes) {
563
+ this.log.verbose(inspect(quote.payload));
564
+ }
565
+ // ensure these quotes are still valid for the slot and have the contract validate them
566
+ const validQuotesPromise = Promise.all(
567
+ quotes.filter(x => x.payload.validUntilSlot >= slotNumber).map(x => this.publisher.validateProofQuote(x)),
568
+ );
569
+
570
+ const validQuotes = (await validQuotesPromise).filter((q): q is EpochProofQuote => !!q);
571
+ if (!validQuotes.length) {
572
+ this.log.verbose(`Failed to find any valid proof quotes`);
573
+ return undefined;
574
+ }
575
+ // pick the quote with the lowest fee
576
+ const sortedQuotes = validQuotes.sort(
577
+ (a: EpochProofQuote, b: EpochProofQuote) => a.payload.basisPointFee - b.payload.basisPointFee,
578
+ );
579
+ return sortedQuotes[0];
580
+ } catch (err) {
581
+ this.log.error(`Failed to create proof claim for previous epoch: ${err}`);
582
+ return undefined;
583
+ }
541
584
  }
542
585
 
543
586
  /**
@@ -547,11 +590,16 @@ export class Sequencer {
547
590
  @trackSpan('Sequencer.publishL2Block', block => ({
548
591
  [Attributes.BLOCK_NUMBER]: block.number,
549
592
  }))
550
- protected async publishL2Block(block: L2Block, attestations?: Signature[], txHashes?: TxHash[]) {
593
+ protected async publishL2Block(
594
+ block: L2Block,
595
+ attestations?: Signature[],
596
+ txHashes?: TxHash[],
597
+ proofQuote?: EpochProofQuote,
598
+ ) {
551
599
  // Publishes new block to the network and awaits the tx to be mined
552
600
  this.state = SequencerState.PUBLISHING_BLOCK;
553
601
 
554
- const publishedL2Block = await this.publisher.proposeL2Block(block, attestations, txHashes);
602
+ const publishedL2Block = await this.publisher.proposeL2Block(block, attestations, txHashes, proofQuote);
555
603
  if (publishedL2Block) {
556
604
  this.lastPublishedBlock = block.number;
557
605
  } else {
@@ -666,12 +714,12 @@ export enum SequencerState {
666
714
  *
667
715
  * @todo: perform this logic within the memory attestation store instead?
668
716
  */
669
- async function orderAttestations(attestations: BlockAttestation[], orderAddresses: EthAddress[]): Promise<Signature[]> {
717
+ function orderAttestations(attestations: BlockAttestation[], orderAddresses: EthAddress[]): Signature[] {
670
718
  // Create a map of sender addresses to BlockAttestations
671
719
  const attestationMap = new Map<string, BlockAttestation>();
672
720
 
673
721
  for (const attestation of attestations) {
674
- const sender = await attestation.getSender();
722
+ const sender = attestation.getSender();
675
723
  if (sender) {
676
724
  attestationMap.set(sender.toString(), attestation);
677
725
  }
@@ -1,8 +1,7 @@
1
- import { PublicKernelType, type Tx, type TxValidator } from '@aztec/circuit-types';
2
- import { type AztecAddress, type Fr } from '@aztec/circuits.js';
1
+ import { PublicKernelPhase, type Tx, type TxValidator } from '@aztec/circuit-types';
2
+ import { type AztecAddress, type Fr, FunctionSelector } from '@aztec/circuits.js';
3
3
  import { createDebugLogger } from '@aztec/foundation/log';
4
- import { FeeJuiceArtifact } from '@aztec/protocol-contracts/fee-juice';
5
- import { AbstractPhaseManager, computeFeePayerBalanceStorageSlot } from '@aztec/simulator';
4
+ import { EnqueuedCallsProcessor, computeFeePayerBalanceStorageSlot } from '@aztec/simulator';
6
5
 
7
6
  /** Provides a view into public contract state */
8
7
  export interface PublicStateSource {
@@ -34,6 +33,10 @@ export class GasTxValidator implements TxValidator<Tx> {
34
33
  return [validTxs, invalidTxs];
35
34
  }
36
35
 
36
+ validateTx(tx: Tx): Promise<boolean> {
37
+ return this.#validateTxFee(tx);
38
+ }
39
+
37
40
  async #validateTxFee(tx: Tx): Promise<boolean> {
38
41
  const feePayer = tx.data.feePayer;
39
42
  // TODO(@spalladino) Eventually remove the is_zero condition as we should always charge fees to every tx
@@ -55,20 +58,20 @@ export class GasTxValidator implements TxValidator<Tx> {
55
58
  );
56
59
 
57
60
  // If there is a claim in this tx that increases the fee payer balance in Fee Juice, add it to balance
58
- const { [PublicKernelType.SETUP]: setupFns } = AbstractPhaseManager.extractEnqueuedPublicCallsByPhase(tx);
61
+ const setupFns = EnqueuedCallsProcessor.getExecutionRequestsByPhase(tx, PublicKernelPhase.SETUP);
59
62
  const claimFunctionCall = setupFns.find(
60
63
  fn =>
61
64
  fn.contractAddress.equals(this.#feeJuiceAddress) &&
62
65
  fn.callContext.msgSender.equals(this.#feeJuiceAddress) &&
63
- fn.callContext.functionSelector.equals(
64
- FeeJuiceArtifact.functions.find(f => f.name === '_increase_public_balance')!,
65
- ) &&
66
- fn.args[0].equals(feePayer) &&
66
+ fn.args.length > 2 &&
67
+ // Public functions get routed through the dispatch function, whose first argument is the target function selector.
68
+ fn.args[0].equals(FunctionSelector.fromSignature('_increase_public_balance((Field),Field)').toField()) &&
69
+ fn.args[1].equals(feePayer) &&
67
70
  !fn.callContext.isStaticCall &&
68
71
  !fn.callContext.isDelegateCall,
69
72
  );
70
73
 
71
- const balance = claimFunctionCall ? initialBalance.add(claimFunctionCall.args[1]) : initialBalance;
74
+ const balance = claimFunctionCall ? initialBalance.add(claimFunctionCall.args[2]) : initialBalance;
72
75
  if (balance.lt(feeLimit)) {
73
76
  this.#log.info(`Rejecting transaction due to not enough fee payer balance`, { feePayer, balance, feeLimit });
74
77
  return false;
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  type AllowedElement,
3
3
  type PublicExecutionRequest,
4
- PublicKernelType,
4
+ PublicKernelPhase,
5
5
  Tx,
6
6
  type TxValidator,
7
7
  } from '@aztec/circuit-types';
8
8
  import { createDebugLogger } from '@aztec/foundation/log';
9
- import { AbstractPhaseManager, ContractsDataSourcePublicDB } from '@aztec/simulator';
9
+ import { ContractsDataSourcePublicDB, EnqueuedCallsProcessor } from '@aztec/simulator';
10
10
  import { type ContractDataSource } from '@aztec/types/contracts';
11
11
 
12
12
  export class PhasesTxValidator implements TxValidator<Tx> {
@@ -27,7 +27,7 @@ export class PhasesTxValidator implements TxValidator<Tx> {
27
27
  // which is what we're trying to do as part of the current txs.
28
28
  await this.contractDataSource.addNewContracts(tx);
29
29
 
30
- if (await this.#validateTx(tx)) {
30
+ if (await this.validateTx(tx)) {
31
31
  validTxs.push(tx);
32
32
  } else {
33
33
  invalidTxs.push(tx);
@@ -39,14 +39,13 @@ export class PhasesTxValidator implements TxValidator<Tx> {
39
39
  return Promise.resolve([validTxs, invalidTxs]);
40
40
  }
41
41
 
42
- async #validateTx(tx: Tx): Promise<boolean> {
42
+ async validateTx(tx: Tx): Promise<boolean> {
43
43
  if (!tx.data.forPublic) {
44
44
  this.#log.debug(`Tx ${Tx.getHash(tx)} does not contain enqueued public functions. Skipping phases validation.`);
45
45
  return true;
46
46
  }
47
47
 
48
- const { [PublicKernelType.SETUP]: setupFns } = AbstractPhaseManager.extractEnqueuedPublicCallsByPhase(tx);
49
-
48
+ const setupFns = EnqueuedCallsProcessor.getExecutionRequestsByPhase(tx, PublicKernelPhase.SETUP);
50
49
  for (const setupFn of setupFns) {
51
50
  if (!(await this.isOnAllowList(setupFn, this.setupAllowList))) {
52
51
  this.#log.warn(
@@ -32,9 +32,9 @@ function patchFn(
32
32
  tx.enqueuedPublicFunctionCalls[index] = fn;
33
33
 
34
34
  const request = tx.data.forPublic![where].publicCallStack[index];
35
- request.item.contractAddress = fn.contractAddress;
36
- request.item.callContext = fn.callContext;
37
- request.item.argsHash = computeVarArgsHash(fn.args);
35
+ request.contractAddress = fn.contractAddress;
36
+ request.callContext = fn.callContext;
37
+ request.argsHash = computeVarArgsHash(fn.args);
38
38
  tx.data.forPublic![where].publicCallStack[index] = request;
39
39
 
40
40
  return {
@@ -2,7 +2,7 @@ import { type AllowedElement, type ProcessedTx, type Tx, type TxValidator } from
2
2
  import { type GlobalVariables } from '@aztec/circuits.js';
3
3
  import { AggregateTxValidator, DataTxValidator, DoubleSpendTxValidator, MetadataTxValidator } from '@aztec/p2p';
4
4
  import { FeeJuiceAddress } from '@aztec/protocol-contracts/fee-juice';
5
- import { WorldStateDB, WorldStatePublicDB } from '@aztec/simulator';
5
+ import { WorldStateDB } from '@aztec/simulator';
6
6
  import { type ContractDataSource } from '@aztec/types/contracts';
7
7
  import { type MerkleTreeOperations } from '@aztec/world-state';
8
8
 
@@ -17,16 +17,17 @@ export class TxValidatorFactory {
17
17
  ) {}
18
18
 
19
19
  validatorForNewTxs(globalVariables: GlobalVariables, setupAllowList: AllowedElement[]): TxValidator<Tx> {
20
+ const worldStateDB = new WorldStateDB(this.merkleTreeDb, this.contractDataSource);
20
21
  return new AggregateTxValidator(
21
22
  new DataTxValidator(),
22
23
  new MetadataTxValidator(globalVariables.chainId, globalVariables.blockNumber),
23
- new DoubleSpendTxValidator(new WorldStateDB(this.merkleTreeDb)),
24
+ new DoubleSpendTxValidator(worldStateDB),
24
25
  new PhasesTxValidator(this.contractDataSource, setupAllowList),
25
- new GasTxValidator(new WorldStatePublicDB(this.merkleTreeDb), FeeJuiceAddress, this.enforceFees),
26
+ new GasTxValidator(worldStateDB, FeeJuiceAddress, this.enforceFees),
26
27
  );
27
28
  }
28
29
 
29
30
  validatorForProcessedTxs(): TxValidator<ProcessedTx> {
30
- return new DoubleSpendTxValidator(new WorldStateDB(this.merkleTreeDb));
31
+ return new DoubleSpendTxValidator(new WorldStateDB(this.merkleTreeDb, this.contractDataSource));
31
32
  }
32
33
  }