@aztec/sequencer-client 0.0.0-test.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 (110) hide show
  1. package/README.md +45 -0
  2. package/dest/client/index.d.ts +2 -0
  3. package/dest/client/index.d.ts.map +1 -0
  4. package/dest/client/index.js +1 -0
  5. package/dest/client/sequencer-client.d.ts +71 -0
  6. package/dest/client/sequencer-client.d.ts.map +1 -0
  7. package/dest/client/sequencer-client.js +117 -0
  8. package/dest/config.d.ts +29 -0
  9. package/dest/config.d.ts.map +1 -0
  10. package/dest/config.js +143 -0
  11. package/dest/global_variable_builder/global_builder.d.ts +32 -0
  12. package/dest/global_variable_builder/global_builder.d.ts.map +1 -0
  13. package/dest/global_variable_builder/global_builder.js +79 -0
  14. package/dest/global_variable_builder/index.d.ts +2 -0
  15. package/dest/global_variable_builder/index.d.ts.map +1 -0
  16. package/dest/global_variable_builder/index.js +1 -0
  17. package/dest/index.d.ts +8 -0
  18. package/dest/index.d.ts.map +1 -0
  19. package/dest/index.js +9 -0
  20. package/dest/publisher/config.d.ts +31 -0
  21. package/dest/publisher/config.d.ts.map +1 -0
  22. package/dest/publisher/config.js +35 -0
  23. package/dest/publisher/index.d.ts +2 -0
  24. package/dest/publisher/index.d.ts.map +1 -0
  25. package/dest/publisher/index.js +1 -0
  26. package/dest/publisher/sequencer-publisher-metrics.d.ts +25 -0
  27. package/dest/publisher/sequencer-publisher-metrics.d.ts.map +1 -0
  28. package/dest/publisher/sequencer-publisher-metrics.js +129 -0
  29. package/dest/publisher/sequencer-publisher.d.ts +152 -0
  30. package/dest/publisher/sequencer-publisher.d.ts.map +1 -0
  31. package/dest/publisher/sequencer-publisher.js +481 -0
  32. package/dest/sequencer/allowed.d.ts +3 -0
  33. package/dest/sequencer/allowed.d.ts.map +1 -0
  34. package/dest/sequencer/allowed.js +27 -0
  35. package/dest/sequencer/config.d.ts +2 -0
  36. package/dest/sequencer/config.d.ts.map +1 -0
  37. package/dest/sequencer/config.js +1 -0
  38. package/dest/sequencer/index.d.ts +4 -0
  39. package/dest/sequencer/index.d.ts.map +1 -0
  40. package/dest/sequencer/index.js +3 -0
  41. package/dest/sequencer/metrics.d.ts +24 -0
  42. package/dest/sequencer/metrics.d.ts.map +1 -0
  43. package/dest/sequencer/metrics.js +102 -0
  44. package/dest/sequencer/sequencer.d.ts +180 -0
  45. package/dest/sequencer/sequencer.d.ts.map +1 -0
  46. package/dest/sequencer/sequencer.js +623 -0
  47. package/dest/sequencer/timetable.d.ts +38 -0
  48. package/dest/sequencer/timetable.d.ts.map +1 -0
  49. package/dest/sequencer/timetable.js +110 -0
  50. package/dest/sequencer/utils.d.ts +48 -0
  51. package/dest/sequencer/utils.d.ts.map +1 -0
  52. package/dest/sequencer/utils.js +53 -0
  53. package/dest/slasher/factory.d.ts +7 -0
  54. package/dest/slasher/factory.d.ts.map +1 -0
  55. package/dest/slasher/factory.js +8 -0
  56. package/dest/slasher/index.d.ts +3 -0
  57. package/dest/slasher/index.d.ts.map +1 -0
  58. package/dest/slasher/index.js +2 -0
  59. package/dest/slasher/slasher_client.d.ts +75 -0
  60. package/dest/slasher/slasher_client.d.ts.map +1 -0
  61. package/dest/slasher/slasher_client.js +132 -0
  62. package/dest/test/index.d.ts +17 -0
  63. package/dest/test/index.d.ts.map +1 -0
  64. package/dest/test/index.js +10 -0
  65. package/dest/tx_validator/archive_cache.d.ts +14 -0
  66. package/dest/tx_validator/archive_cache.d.ts.map +1 -0
  67. package/dest/tx_validator/archive_cache.js +22 -0
  68. package/dest/tx_validator/gas_validator.d.ts +14 -0
  69. package/dest/tx_validator/gas_validator.d.ts.map +1 -0
  70. package/dest/tx_validator/gas_validator.js +78 -0
  71. package/dest/tx_validator/nullifier_cache.d.ts +16 -0
  72. package/dest/tx_validator/nullifier_cache.d.ts.map +1 -0
  73. package/dest/tx_validator/nullifier_cache.js +24 -0
  74. package/dest/tx_validator/phases_validator.d.ts +12 -0
  75. package/dest/tx_validator/phases_validator.d.ts.map +1 -0
  76. package/dest/tx_validator/phases_validator.js +80 -0
  77. package/dest/tx_validator/test_utils.d.ts +23 -0
  78. package/dest/tx_validator/test_utils.d.ts.map +1 -0
  79. package/dest/tx_validator/test_utils.js +26 -0
  80. package/dest/tx_validator/tx_validator_factory.d.ts +18 -0
  81. package/dest/tx_validator/tx_validator_factory.d.ts.map +1 -0
  82. package/dest/tx_validator/tx_validator_factory.js +50 -0
  83. package/package.json +121 -0
  84. package/src/client/index.ts +1 -0
  85. package/src/client/sequencer-client.ts +219 -0
  86. package/src/config.ts +179 -0
  87. package/src/global_variable_builder/global_builder.ts +108 -0
  88. package/src/global_variable_builder/index.ts +1 -0
  89. package/src/index.ts +10 -0
  90. package/src/publisher/config.ts +75 -0
  91. package/src/publisher/index.ts +1 -0
  92. package/src/publisher/sequencer-publisher-metrics.ts +176 -0
  93. package/src/publisher/sequencer-publisher.ts +625 -0
  94. package/src/sequencer/allowed.ts +36 -0
  95. package/src/sequencer/config.ts +1 -0
  96. package/src/sequencer/index.ts +3 -0
  97. package/src/sequencer/metrics.ts +137 -0
  98. package/src/sequencer/sequencer.ts +759 -0
  99. package/src/sequencer/timetable.ts +123 -0
  100. package/src/sequencer/utils.ts +74 -0
  101. package/src/slasher/factory.ts +15 -0
  102. package/src/slasher/index.ts +2 -0
  103. package/src/slasher/slasher_client.ts +193 -0
  104. package/src/test/index.ts +20 -0
  105. package/src/tx_validator/archive_cache.ts +28 -0
  106. package/src/tx_validator/gas_validator.ts +101 -0
  107. package/src/tx_validator/nullifier_cache.ts +30 -0
  108. package/src/tx_validator/phases_validator.ts +98 -0
  109. package/src/tx_validator/test_utils.ts +48 -0
  110. package/src/tx_validator/tx_validator_factory.ts +120 -0
package/README.md ADDED
@@ -0,0 +1,45 @@
1
+ # Sequencer Client
2
+
3
+ The sequencer is a module responsible for creating and publishing new rollup blocks. This involves fetching txs from the P2P pool, ordering them, executing any public functions, running them through the rollup circuits, assembling the L2 block, and posting it to the L1 rollup contract along with any contract deployment public data.
4
+
5
+ The client itself is implemented as a continuous loop. After being started, it polls the P2P pool for new txs, assembles a new block if any is found, and goes back to sleep. On every new block assembled, it modifies the world state database to reflect the txs processed, but these changes are only committed once the world state synchronizer sees the new block on L1.
6
+
7
+ ## Components
8
+
9
+ _What components are used to build a sequencer client._
10
+
11
+ - The **block builder** is responsible for assembling an L2 block out of a set of processed transactions (we say a tx has been _processed_ if all its function calls have been executed). This involves running the txs through the base, merge, and rollup circuits, updating the world state trees, and building the L2 block object.
12
+
13
+ - The **prover** generates proofs for every circuit used. For the time being, no proofs are being actually generated, so the only implementation is an empty one.
14
+
15
+ - The **publisher** deals with sending L1 transactions to the rollup and contract deployment emitter contracts. It is responsible for assembling the Ethereum tx, choosing reasonable gas settings, and monitoring the tx until it gets mined. Note that the current implementation does not handle unstable network conditions (gas price spikes, reorgs, etc).
16
+
17
+ - The **public processor** executes any public function calls in the transactions. Unlike private function calls, which are resolved in the client, public functions require access to the latest data trees, so they are executed by the sequencer, much like in any non-private L2.
18
+
19
+ - The **simulator** is an interface to the wasm implementations of the circuits used by the sequencer.
20
+
21
+ - The **sequencer** pulls txs from the P2P pool, orchestrates all the components above to assemble and publish a block, and updates the world state database.
22
+
23
+ ## Circuits
24
+
25
+ _What circuits does the sequencer depend on._
26
+
27
+ - The **public circuit** is responsible for proving the execution of Brillig (public function bytecode). At the moment, we are using a fake version that actually runs ACIR (intermediate representation for private functions) and does not emit any proofs.
28
+
29
+ - The **public kernel circuit** then validates the output of the public circuit, and outputs a set of changes to the world state in the same format as the private kernel circuit, meaning we get a standard representation for all txs, regardless of whether public or private functions (or both) were run. The kernel circuits are run iteratively for every recursive call in the transaction.
30
+
31
+ - The **base rollup circuit** aggregates the changes from two txs (more precisely, the outputs from their kernel circuits once all call stacks are emptied) into a single output.
32
+
33
+ - The **merge rollup circuit** aggregates two outputs from base rollup circuits into a single one. This circuit is executed recursively until only two outputs are left. This setup means that an L2 block needs to contain always a power-of-two number of txs; if there are not enough, then empty txs are added.
34
+
35
+ - The **root rollup circuit** consumes two outputs from base or merge rollups and outputs the data to assemble an L2 block. The L1 rollup contract then verifies the proof from this circuit, which implies that all txs included in it were correct.
36
+
37
+ ## Development
38
+
39
+ Start by running `bootstrap.sh` in the project root.
40
+
41
+ To build the package, run `yarn build` in the root.
42
+
43
+ To watch for changes, `yarn build:dev`.
44
+
45
+ To run the tests, execute `yarn test`.
@@ -0,0 +1,2 @@
1
+ export * from './sequencer-client.js';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAC"}
@@ -0,0 +1 @@
1
+ export * from './sequencer-client.js';
@@ -0,0 +1,71 @@
1
+ import type { BlobSinkClientInterface } from '@aztec/blob-sink/client';
2
+ import { EpochCache } from '@aztec/epoch-cache';
3
+ import { L1TxUtilsWithBlobs } from '@aztec/ethereum/l1-tx-utils-with-blobs';
4
+ import { EthAddress } from '@aztec/foundation/eth-address';
5
+ import type { DateProvider } from '@aztec/foundation/timer';
6
+ import type { P2P } from '@aztec/p2p';
7
+ import type { AztecAddress } from '@aztec/stdlib/aztec-address';
8
+ import type { L2BlockSource } from '@aztec/stdlib/block';
9
+ import type { ContractDataSource } from '@aztec/stdlib/contract';
10
+ import type { WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server';
11
+ import type { L1ToL2MessageSource } from '@aztec/stdlib/messaging';
12
+ import type { TelemetryClient } from '@aztec/telemetry-client';
13
+ import type { ValidatorClient } from '@aztec/validator-client';
14
+ import type { SequencerClientConfig } from '../config.js';
15
+ import { SequencerPublisher } from '../publisher/index.js';
16
+ import { Sequencer, type SequencerConfig } from '../sequencer/index.js';
17
+ import type { SlasherClient } from '../slasher/index.js';
18
+ /**
19
+ * Encapsulates the full sequencer and publisher.
20
+ */
21
+ export declare class SequencerClient {
22
+ protected sequencer: Sequencer;
23
+ constructor(sequencer: Sequencer);
24
+ /**
25
+ * Initializes and starts a new instance.
26
+ * @param config - Configuration for the sequencer, publisher, and L1 tx sender.
27
+ * @param p2pClient - P2P client that provides the txs to be sequenced.
28
+ * @param validatorClient - Validator client performs attestation duties when rotating proposers.
29
+ * @param worldStateSynchronizer - Provides access to world state.
30
+ * @param contractDataSource - Provides access to contract bytecode for public executions.
31
+ * @param l2BlockSource - Provides information about the previously published blocks.
32
+ * @param l1ToL2MessageSource - Provides access to L1 to L2 messages.
33
+ * @param prover - An instance of a block prover
34
+ * @param simulationProvider - An instance of a simulation provider
35
+ * @returns A new running instance.
36
+ */
37
+ static new(config: SequencerClientConfig, deps: {
38
+ validatorClient: ValidatorClient | undefined;
39
+ p2pClient: P2P;
40
+ worldStateSynchronizer: WorldStateSynchronizer;
41
+ slasherClient: SlasherClient;
42
+ contractDataSource: ContractDataSource;
43
+ l2BlockSource: L2BlockSource;
44
+ l1ToL2MessageSource: L1ToL2MessageSource;
45
+ telemetry: TelemetryClient;
46
+ publisher?: SequencerPublisher;
47
+ blobSinkClient?: BlobSinkClientInterface;
48
+ dateProvider: DateProvider;
49
+ epochCache?: EpochCache;
50
+ l1TxUtils?: L1TxUtilsWithBlobs;
51
+ }): Promise<SequencerClient>;
52
+ /**
53
+ * Updates sequencer config.
54
+ * @param config - New parameters.
55
+ */
56
+ updateSequencerConfig(config: SequencerConfig): Promise<void>;
57
+ /**
58
+ * Stops the sequencer from processing new txs.
59
+ */
60
+ stop(): Promise<void>;
61
+ /** Forces the sequencer to bypass all time and tx count checks for the next block and build anyway. */
62
+ flush(): void;
63
+ /**
64
+ * Restarts the sequencer after being stopped.
65
+ */
66
+ restart(): void;
67
+ get coinbase(): EthAddress;
68
+ get feeRecipient(): AztecAddress;
69
+ get forwarderAddress(): EthAddress;
70
+ }
71
+ //# sourceMappingURL=sequencer-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sequencer-client.d.ts","sourceRoot":"","sources":["../../src/client/sequencer-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AACvE,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAUhD,OAAO,EAAE,kBAAkB,EAAE,MAAM,wCAAwC,CAAC;AAC5E,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAE3D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,YAAY,CAAC;AAGtC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AAC9E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AACnE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAE/D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAE1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAAE,SAAS,EAAE,KAAK,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD;;GAEG;AACH,qBAAa,eAAe;IACd,SAAS,CAAC,SAAS,EAAE,SAAS;gBAApB,SAAS,EAAE,SAAS;IAE1C;;;;;;;;;;;;OAYG;WACiB,GAAG,CACrB,MAAM,EAAE,qBAAqB,EAC7B,IAAI,EAAE;QACJ,eAAe,EAAE,eAAe,GAAG,SAAS,CAAC;QAC7C,SAAS,EAAE,GAAG,CAAC;QACf,sBAAsB,EAAE,sBAAsB,CAAC;QAC/C,aAAa,EAAE,aAAa,CAAC;QAC7B,kBAAkB,EAAE,kBAAkB,CAAC;QACvC,aAAa,EAAE,aAAa,CAAC;QAC7B,mBAAmB,EAAE,mBAAmB,CAAC;QACzC,SAAS,EAAE,eAAe,CAAC;QAC3B,SAAS,CAAC,EAAE,kBAAkB,CAAC;QAC/B,cAAc,CAAC,EAAE,uBAAuB,CAAC;QACzC,YAAY,EAAE,YAAY,CAAC;QAC3B,UAAU,CAAC,EAAE,UAAU,CAAC;QACxB,SAAS,CAAC,EAAE,kBAAkB,CAAC;KAChC;IAiHH;;;OAGG;IACI,qBAAqB,CAAC,MAAM,EAAE,eAAe;IAIpD;;OAEG;IACU,IAAI;IAIjB,uGAAuG;IAChG,KAAK;IAIZ;;OAEG;IACI,OAAO;IAId,IAAI,QAAQ,IAAI,UAAU,CAEzB;IAED,IAAI,YAAY,IAAI,YAAY,CAE/B;IAED,IAAI,gBAAgB,IAAI,UAAU,CAEjC;CACF"}
@@ -0,0 +1,117 @@
1
+ import { EpochCache } from '@aztec/epoch-cache';
2
+ import { ForwarderContract, GovernanceProposerContract, RollupContract, SlashingProposerContract, createEthereumChain, createL1Clients, isAnvilTestChain } from '@aztec/ethereum';
3
+ import { L1TxUtilsWithBlobs } from '@aztec/ethereum/l1-tx-utils-with-blobs';
4
+ import { EthAddress } from '@aztec/foundation/eth-address';
5
+ import { createLogger } from '@aztec/foundation/log';
6
+ import { LightweightBlockBuilderFactory } from '@aztec/prover-client/block-builder';
7
+ import { PublicProcessorFactory } from '@aztec/simulator/server';
8
+ import { GlobalVariableBuilder } from '../global_variable_builder/index.js';
9
+ import { SequencerPublisher } from '../publisher/index.js';
10
+ import { Sequencer } from '../sequencer/index.js';
11
+ /**
12
+ * Encapsulates the full sequencer and publisher.
13
+ */ export class SequencerClient {
14
+ sequencer;
15
+ constructor(sequencer){
16
+ this.sequencer = sequencer;
17
+ }
18
+ /**
19
+ * Initializes and starts a new instance.
20
+ * @param config - Configuration for the sequencer, publisher, and L1 tx sender.
21
+ * @param p2pClient - P2P client that provides the txs to be sequenced.
22
+ * @param validatorClient - Validator client performs attestation duties when rotating proposers.
23
+ * @param worldStateSynchronizer - Provides access to world state.
24
+ * @param contractDataSource - Provides access to contract bytecode for public executions.
25
+ * @param l2BlockSource - Provides information about the previously published blocks.
26
+ * @param l1ToL2MessageSource - Provides access to L1 to L2 messages.
27
+ * @param prover - An instance of a block prover
28
+ * @param simulationProvider - An instance of a simulation provider
29
+ * @returns A new running instance.
30
+ */ static async new(config, deps) {
31
+ const { validatorClient, p2pClient, worldStateSynchronizer, slasherClient, contractDataSource, l2BlockSource, l1ToL2MessageSource, telemetry: telemetryClient } = deps;
32
+ const { l1RpcUrls: rpcUrls, l1ChainId: chainId, publisherPrivateKey } = config;
33
+ const chain = createEthereumChain(rpcUrls, chainId);
34
+ const log = createLogger('sequencer-client');
35
+ const { publicClient, walletClient } = createL1Clients(rpcUrls, publisherPrivateKey, chain.chainInfo);
36
+ const l1TxUtils = deps.l1TxUtils ?? new L1TxUtilsWithBlobs(publicClient, walletClient, log, config);
37
+ const rollupContract = new RollupContract(publicClient, config.l1Contracts.rollupAddress.toString());
38
+ const [l1GenesisTime, slotDuration] = await Promise.all([
39
+ rollupContract.getL1GenesisTime(),
40
+ rollupContract.getSlotDuration()
41
+ ]);
42
+ const forwarderContract = config.customForwarderContractAddress && config.customForwarderContractAddress !== EthAddress.ZERO ? new ForwarderContract(publicClient, config.customForwarderContractAddress.toString(), config.l1Contracts.rollupAddress.toString()) : await ForwarderContract.create(walletClient.account.address, walletClient, publicClient, log, config.l1Contracts.rollupAddress.toString());
43
+ const governanceProposerContract = new GovernanceProposerContract(publicClient, config.l1Contracts.governanceProposerAddress.toString());
44
+ const slashingProposerAddress = await rollupContract.getSlashingProposerAddress();
45
+ const slashingProposerContract = new SlashingProposerContract(publicClient, slashingProposerAddress.toString());
46
+ const epochCache = deps.epochCache ?? await EpochCache.create(config.l1Contracts.rollupAddress, {
47
+ l1RpcUrls: rpcUrls,
48
+ l1ChainId: chainId,
49
+ viemPollingIntervalMS: config.viemPollingIntervalMS,
50
+ aztecSlotDuration: config.aztecSlotDuration,
51
+ ethereumSlotDuration: config.ethereumSlotDuration,
52
+ aztecEpochDuration: config.aztecEpochDuration
53
+ }, {
54
+ dateProvider: deps.dateProvider
55
+ });
56
+ const publisher = deps.publisher ?? new SequencerPublisher(config, {
57
+ l1TxUtils,
58
+ telemetry: telemetryClient,
59
+ blobSinkClient: deps.blobSinkClient,
60
+ rollupContract,
61
+ epochCache,
62
+ forwarderContract,
63
+ governanceProposerContract,
64
+ slashingProposerContract
65
+ });
66
+ const globalsBuilder = new GlobalVariableBuilder(config);
67
+ const publicProcessorFactory = new PublicProcessorFactory(contractDataSource, deps.dateProvider, telemetryClient);
68
+ const ethereumSlotDuration = config.ethereumSlotDuration;
69
+ // When running in anvil, assume we can post a tx up until the very last second of an L1 slot.
70
+ // Otherwise, assume we must have broadcasted the tx before the slot started (we use a default
71
+ // maxL1TxInclusionTimeIntoSlot of zero) to get the tx into that L1 slot.
72
+ // In theory, the L1 slot has an initial 4s phase where the block is propagated, so we could
73
+ // make it with a propagation time into slot equal to 4s. However, we prefer being conservative.
74
+ // See https://www.blocknative.com/blog/anatomy-of-a-slot#7 for more info.
75
+ const maxL1TxInclusionTimeIntoSlot = config.maxL1TxInclusionTimeIntoSlot ?? isAnvilTestChain(config.l1ChainId) ? ethereumSlotDuration : 0;
76
+ const l1Constants = {
77
+ l1GenesisTime,
78
+ slotDuration: Number(slotDuration),
79
+ ethereumSlotDuration
80
+ };
81
+ const sequencer = new Sequencer(publisher, validatorClient, globalsBuilder, p2pClient, worldStateSynchronizer, slasherClient, new LightweightBlockBuilderFactory(telemetryClient), l2BlockSource, l1ToL2MessageSource, publicProcessorFactory, contractDataSource, l1Constants, deps.dateProvider, {
82
+ ...config,
83
+ maxL1TxInclusionTimeIntoSlot
84
+ }, telemetryClient);
85
+ await validatorClient?.start();
86
+ await sequencer.start();
87
+ return new SequencerClient(sequencer);
88
+ }
89
+ /**
90
+ * Updates sequencer config.
91
+ * @param config - New parameters.
92
+ */ updateSequencerConfig(config) {
93
+ return this.sequencer.updateConfig(config);
94
+ }
95
+ /**
96
+ * Stops the sequencer from processing new txs.
97
+ */ async stop() {
98
+ await this.sequencer.stop();
99
+ }
100
+ /** Forces the sequencer to bypass all time and tx count checks for the next block and build anyway. */ flush() {
101
+ this.sequencer.flush();
102
+ }
103
+ /**
104
+ * Restarts the sequencer after being stopped.
105
+ */ restart() {
106
+ this.sequencer.restart();
107
+ }
108
+ get coinbase() {
109
+ return this.sequencer.coinbase;
110
+ }
111
+ get feeRecipient() {
112
+ return this.sequencer.feeRecipient;
113
+ }
114
+ get forwarderAddress() {
115
+ return this.sequencer.getForwarderAddress();
116
+ }
117
+ }
@@ -0,0 +1,29 @@
1
+ import { type L1ContractsConfig, type L1ReaderConfig } from '@aztec/ethereum';
2
+ import { type ConfigMappingsType } from '@aztec/foundation/config';
3
+ import { type AllowedElement, type ChainConfig, type SequencerConfig } from '@aztec/stdlib/config';
4
+ import { type PublisherConfig, type TxSenderConfig } from './publisher/config.js';
5
+ export * from './publisher/config.js';
6
+ export type { SequencerConfig };
7
+ /**
8
+ * Configuration settings for the SequencerClient.
9
+ */
10
+ export type SequencerClientConfig = PublisherConfig & TxSenderConfig & SequencerConfig & L1ReaderConfig & ChainConfig & Pick<L1ContractsConfig, 'ethereumSlotDuration' | 'aztecSlotDuration' | 'aztecEpochDuration'>;
11
+ export declare const sequencerConfigMappings: ConfigMappingsType<SequencerConfig>;
12
+ export declare const sequencerClientConfigMappings: ConfigMappingsType<SequencerClientConfig>;
13
+ /**
14
+ * Creates an instance of SequencerClientConfig out of environment variables using sensible defaults for integration testing if not set.
15
+ */
16
+ export declare function getConfigEnvVars(): SequencerClientConfig;
17
+ /**
18
+ * Parses a string to a list of allowed elements.
19
+ * Each encoded is expected to be of one of the following formats
20
+ * `I:${address}`
21
+ * `I:${address}:${selector}`
22
+ * `C:${classId}`
23
+ * `C:${classId}:${selector}`
24
+ *
25
+ * @param value The string to parse
26
+ * @returns A list of allowed elements
27
+ */
28
+ export declare function parseSequencerAllowList(value: string): AllowedElement[];
29
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,iBAAiB,EACtB,KAAK,cAAc,EAGpB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,KAAK,kBAAkB,EAKxB,MAAM,0BAA0B,CAAC;AAKlC,OAAO,EAAE,KAAK,cAAc,EAAE,KAAK,WAAW,EAAE,KAAK,eAAe,EAAuB,MAAM,sBAAsB,CAAC;AAExH,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,cAAc,EAGpB,MAAM,uBAAuB,CAAC;AAE/B,cAAc,uBAAuB,CAAC;AACtC,YAAY,EAAE,eAAe,EAAE,CAAC;AAEhC;;GAEG;AACH,MAAM,MAAM,qBAAqB,GAAG,eAAe,GACjD,cAAc,GACd,eAAe,GACf,cAAc,GACd,WAAW,GACX,IAAI,CAAC,iBAAiB,EAAE,sBAAsB,GAAG,mBAAmB,GAAG,oBAAoB,CAAC,CAAC;AAE/F,eAAO,MAAM,uBAAuB,EAAE,kBAAkB,CAAC,eAAe,CAyEvE,CAAC;AAEF,eAAO,MAAM,6BAA6B,EAAE,kBAAkB,CAAC,qBAAqB,CAOnF,CAAC;AAEF;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,qBAAqB,CAExD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc,EAAE,CAqCvE"}
package/dest/config.js ADDED
@@ -0,0 +1,143 @@
1
+ import { l1ContractsConfigMappings, l1ReaderConfigMappings } from '@aztec/ethereum';
2
+ import { booleanConfigHelper, getConfigFromMappings, numberConfigHelper, pickConfigMappings } from '@aztec/foundation/config';
3
+ import { EthAddress } from '@aztec/foundation/eth-address';
4
+ import { Fr } from '@aztec/foundation/fields';
5
+ import { FunctionSelector } from '@aztec/stdlib/abi';
6
+ import { AztecAddress } from '@aztec/stdlib/aztec-address';
7
+ import { chainConfigMappings } from '@aztec/stdlib/config';
8
+ import { getPublisherConfigMappings, getTxSenderConfigMappings } from './publisher/config.js';
9
+ export * from './publisher/config.js';
10
+ export const sequencerConfigMappings = {
11
+ transactionPollingIntervalMS: {
12
+ env: 'SEQ_TX_POLLING_INTERVAL_MS',
13
+ description: 'The number of ms to wait between polling for pending txs.',
14
+ ...numberConfigHelper(500)
15
+ },
16
+ maxTxsPerBlock: {
17
+ env: 'SEQ_MAX_TX_PER_BLOCK',
18
+ description: 'The maximum number of txs to include in a block.',
19
+ ...numberConfigHelper(32)
20
+ },
21
+ minTxsPerBlock: {
22
+ env: 'SEQ_MIN_TX_PER_BLOCK',
23
+ description: 'The minimum number of txs to include in a block.',
24
+ ...numberConfigHelper(1)
25
+ },
26
+ maxL2BlockGas: {
27
+ env: 'SEQ_MAX_L2_BLOCK_GAS',
28
+ description: 'The maximum L2 block gas.',
29
+ ...numberConfigHelper(10e9)
30
+ },
31
+ maxDABlockGas: {
32
+ env: 'SEQ_MAX_DA_BLOCK_GAS',
33
+ description: 'The maximum DA block gas.',
34
+ ...numberConfigHelper(10e9)
35
+ },
36
+ coinbase: {
37
+ env: 'COINBASE',
38
+ parseEnv: (val)=>val ? EthAddress.fromString(val) : undefined,
39
+ description: 'Recipient of block reward.'
40
+ },
41
+ feeRecipient: {
42
+ env: 'FEE_RECIPIENT',
43
+ parseEnv: (val)=>AztecAddress.fromString(val),
44
+ description: 'Address to receive fees.'
45
+ },
46
+ acvmWorkingDirectory: {
47
+ env: 'ACVM_WORKING_DIRECTORY',
48
+ description: 'The working directory to use for simulation/proving'
49
+ },
50
+ acvmBinaryPath: {
51
+ env: 'ACVM_BINARY_PATH',
52
+ description: 'The path to the ACVM binary'
53
+ },
54
+ allowedInSetup: {
55
+ env: 'SEQ_ALLOWED_SETUP_FN',
56
+ parseEnv: (val)=>parseSequencerAllowList(val),
57
+ description: 'The list of functions calls allowed to run in setup',
58
+ printDefault: ()=>'AuthRegistry, FeeJuice.increase_public_balance, Token.increase_public_balance, FPC.prepare_fee'
59
+ },
60
+ maxBlockSizeInBytes: {
61
+ env: 'SEQ_MAX_BLOCK_SIZE_IN_BYTES',
62
+ description: 'Max block size',
63
+ ...numberConfigHelper(1024 * 1024)
64
+ },
65
+ enforceTimeTable: {
66
+ env: 'SEQ_ENFORCE_TIME_TABLE',
67
+ description: 'Whether to enforce the time table when building blocks',
68
+ ...booleanConfigHelper(),
69
+ defaultValue: false
70
+ },
71
+ governanceProposerPayload: {
72
+ env: 'GOVERNANCE_PROPOSER_PAYLOAD_ADDRESS',
73
+ description: 'The address of the payload for the governanceProposer',
74
+ parseEnv: (val)=>EthAddress.fromString(val),
75
+ defaultValue: EthAddress.ZERO
76
+ },
77
+ maxL1TxInclusionTimeIntoSlot: {
78
+ env: 'SEQ_MAX_L1_TX_INCLUSION_TIME_INTO_SLOT',
79
+ description: 'How many seconds into an L1 slot we can still send a tx and get it mined.',
80
+ parseEnv: (val)=>val ? parseInt(val, 10) : undefined
81
+ }
82
+ };
83
+ export const sequencerClientConfigMappings = {
84
+ ...sequencerConfigMappings,
85
+ ...l1ReaderConfigMappings,
86
+ ...getTxSenderConfigMappings('SEQ'),
87
+ ...getPublisherConfigMappings('SEQ'),
88
+ ...chainConfigMappings,
89
+ ...pickConfigMappings(l1ContractsConfigMappings, [
90
+ 'ethereumSlotDuration',
91
+ 'aztecSlotDuration',
92
+ 'aztecEpochDuration'
93
+ ])
94
+ };
95
+ /**
96
+ * Creates an instance of SequencerClientConfig out of environment variables using sensible defaults for integration testing if not set.
97
+ */ export function getConfigEnvVars() {
98
+ return getConfigFromMappings(sequencerClientConfigMappings);
99
+ }
100
+ /**
101
+ * Parses a string to a list of allowed elements.
102
+ * Each encoded is expected to be of one of the following formats
103
+ * `I:${address}`
104
+ * `I:${address}:${selector}`
105
+ * `C:${classId}`
106
+ * `C:${classId}:${selector}`
107
+ *
108
+ * @param value The string to parse
109
+ * @returns A list of allowed elements
110
+ */ export function parseSequencerAllowList(value) {
111
+ const entries = [];
112
+ if (!value) {
113
+ return entries;
114
+ }
115
+ for (const val of value.split(',')){
116
+ const [typeString, identifierString, selectorString] = val.split(':');
117
+ const selector = selectorString !== undefined ? FunctionSelector.fromString(selectorString) : undefined;
118
+ if (typeString === 'I') {
119
+ if (selector) {
120
+ entries.push({
121
+ address: AztecAddress.fromString(identifierString),
122
+ selector
123
+ });
124
+ } else {
125
+ entries.push({
126
+ address: AztecAddress.fromString(identifierString)
127
+ });
128
+ }
129
+ } else if (typeString === 'C') {
130
+ if (selector) {
131
+ entries.push({
132
+ classId: Fr.fromHexString(identifierString),
133
+ selector
134
+ });
135
+ } else {
136
+ entries.push({
137
+ classId: Fr.fromHexString(identifierString)
138
+ });
139
+ }
140
+ }
141
+ }
142
+ return entries;
143
+ }
@@ -0,0 +1,32 @@
1
+ import { type L1ContractsConfig, type L1ReaderConfig } from '@aztec/ethereum';
2
+ import type { EthAddress } from '@aztec/foundation/eth-address';
3
+ import { Fr } from '@aztec/foundation/fields';
4
+ import type { AztecAddress } from '@aztec/stdlib/aztec-address';
5
+ import { GasFees } from '@aztec/stdlib/gas';
6
+ import type { GlobalVariableBuilder as GlobalVariableBuilderInterface } from '@aztec/stdlib/tx';
7
+ import { GlobalVariables } from '@aztec/stdlib/tx';
8
+ /**
9
+ * Simple global variables builder.
10
+ */
11
+ export declare class GlobalVariableBuilder implements GlobalVariableBuilderInterface {
12
+ private log;
13
+ private rollupContract;
14
+ private publicClient;
15
+ private ethereumSlotDuration;
16
+ constructor(config: L1ReaderConfig & Pick<L1ContractsConfig, 'ethereumSlotDuration'>);
17
+ /**
18
+ * Computes the "current" base fees, e.g., the price that you currently should pay to get include in the next block
19
+ * @returns Base fees for the expected next block
20
+ */
21
+ getCurrentBaseFees(): Promise<GasFees>;
22
+ /**
23
+ * Simple builder of global variables that use the minimum time possible.
24
+ * @param blockNumber - The block number to build global variables for.
25
+ * @param coinbase - The address to receive block reward.
26
+ * @param feeRecipient - The address to receive fees.
27
+ * @param slotNumber - The slot number to use for the global variables, if undefined it will be calculated.
28
+ * @returns The global variables for the given block number.
29
+ */
30
+ buildGlobalVariables(blockNumber: Fr, coinbase: EthAddress, feeRecipient: AztecAddress, slotNumber?: bigint): Promise<GlobalVariables>;
31
+ }
32
+ //# sourceMappingURL=global_builder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"global_builder.d.ts","sourceRoot":"","sources":["../../src/global_variable_builder/global_builder.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,iBAAiB,EACtB,KAAK,cAAc,EAGpB,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAChE,OAAO,EAAE,EAAE,EAAE,MAAM,0BAA0B,CAAC;AAG9C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAC5C,OAAO,KAAK,EAAE,qBAAqB,IAAI,8BAA8B,EAAE,MAAM,kBAAkB,CAAC;AAChG,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAInD;;GAEG;AACH,qBAAa,qBAAsB,YAAW,8BAA8B;IAC1E,OAAO,CAAC,GAAG,CAAqD;IAEhE,OAAO,CAAC,cAAc,CAA4D;IAClF,OAAO,CAAC,YAAY,CAAmB;IACvC,OAAO,CAAC,oBAAoB,CAAS;gBAEzB,MAAM,EAAE,cAAc,GAAG,IAAI,CAAC,iBAAiB,EAAE,sBAAsB,CAAC;IAoBpF;;;OAGG;IACU,kBAAkB,IAAI,OAAO,CAAC,OAAO,CAAC;IAanD;;;;;;;OAOG;IACU,oBAAoB,CAC/B,WAAW,EAAE,EAAE,EACf,QAAQ,EAAE,UAAU,EACpB,YAAY,EAAE,YAAY,EAC1B,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,eAAe,CAAC;CA8B5B"}
@@ -0,0 +1,79 @@
1
+ import { createEthereumChain } from '@aztec/ethereum';
2
+ import { Fr } from '@aztec/foundation/fields';
3
+ import { createLogger } from '@aztec/foundation/log';
4
+ import { RollupAbi } from '@aztec/l1-artifacts';
5
+ import { GasFees } from '@aztec/stdlib/gas';
6
+ import { GlobalVariables } from '@aztec/stdlib/tx';
7
+ import { createPublicClient, fallback, getAddress, getContract, http } from 'viem';
8
+ /**
9
+ * Simple global variables builder.
10
+ */ export class GlobalVariableBuilder {
11
+ log = createLogger('sequencer:global_variable_builder');
12
+ rollupContract;
13
+ publicClient;
14
+ ethereumSlotDuration;
15
+ constructor(config){
16
+ const { l1RpcUrls, l1ChainId: chainId, l1Contracts } = config;
17
+ const chain = createEthereumChain(l1RpcUrls, chainId);
18
+ this.ethereumSlotDuration = config.ethereumSlotDuration;
19
+ this.publicClient = createPublicClient({
20
+ chain: chain.chainInfo,
21
+ transport: fallback(chain.rpcUrls.map((url)=>http(url))),
22
+ pollingInterval: config.viemPollingIntervalMS
23
+ });
24
+ this.rollupContract = getContract({
25
+ address: getAddress(l1Contracts.rollupAddress.toString()),
26
+ abi: RollupAbi,
27
+ client: this.publicClient
28
+ });
29
+ }
30
+ /**
31
+ * Computes the "current" base fees, e.g., the price that you currently should pay to get include in the next block
32
+ * @returns Base fees for the expected next block
33
+ */ async getCurrentBaseFees() {
34
+ // Since this might be called in the middle of a slot where a block might have been published,
35
+ // we need to fetch the last block written, and estimate the earliest timestamp for the next block.
36
+ // The timestamp of that last block will act as a lower bound for the next block.
37
+ const lastBlock = await this.rollupContract.read.getBlock([
38
+ await this.rollupContract.read.getPendingBlockNumber()
39
+ ]);
40
+ const earliestTimestamp = await this.rollupContract.read.getTimestampForSlot([
41
+ lastBlock.slotNumber + 1n
42
+ ]);
43
+ const nextEthTimestamp = BigInt((await this.publicClient.getBlock()).timestamp + BigInt(this.ethereumSlotDuration));
44
+ const timestamp = earliestTimestamp > nextEthTimestamp ? earliestTimestamp : nextEthTimestamp;
45
+ return new GasFees(Fr.ZERO, new Fr(await this.rollupContract.read.getManaBaseFeeAt([
46
+ timestamp,
47
+ true
48
+ ])));
49
+ }
50
+ /**
51
+ * Simple builder of global variables that use the minimum time possible.
52
+ * @param blockNumber - The block number to build global variables for.
53
+ * @param coinbase - The address to receive block reward.
54
+ * @param feeRecipient - The address to receive fees.
55
+ * @param slotNumber - The slot number to use for the global variables, if undefined it will be calculated.
56
+ * @returns The global variables for the given block number.
57
+ */ async buildGlobalVariables(blockNumber, coinbase, feeRecipient, slotNumber) {
58
+ const version = new Fr(await this.rollupContract.read.getVersion());
59
+ const chainId = new Fr(this.publicClient.chain.id);
60
+ if (slotNumber === undefined) {
61
+ const ts = BigInt((await this.publicClient.getBlock()).timestamp + BigInt(this.ethereumSlotDuration));
62
+ slotNumber = await this.rollupContract.read.getSlotAt([
63
+ ts
64
+ ]);
65
+ }
66
+ const timestamp = await this.rollupContract.read.getTimestampForSlot([
67
+ slotNumber
68
+ ]);
69
+ const slotFr = new Fr(slotNumber);
70
+ const timestampFr = new Fr(timestamp);
71
+ // We can skip much of the logic in getCurrentBaseFees since it we already check that we are not within a slot elsewhere.
72
+ const gasFees = new GasFees(Fr.ZERO, new Fr(await this.rollupContract.read.getManaBaseFeeAt([
73
+ timestamp,
74
+ true
75
+ ])));
76
+ const globalVariables = new GlobalVariables(chainId, version, blockNumber, slotFr, timestampFr, coinbase, feeRecipient, gasFees);
77
+ return globalVariables;
78
+ }
79
+ }
@@ -0,0 +1,2 @@
1
+ export { GlobalVariableBuilder } from './global_builder.js';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/global_variable_builder/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC"}
@@ -0,0 +1 @@
1
+ export { GlobalVariableBuilder } from './global_builder.js';
@@ -0,0 +1,8 @@
1
+ export * from './client/index.js';
2
+ export * from './config.js';
3
+ export * from './publisher/index.js';
4
+ export * from './tx_validator/tx_validator_factory.js';
5
+ export * from './slasher/index.js';
6
+ export { Sequencer, SequencerState, getDefaultAllowedSetupFunctions } from './sequencer/index.js';
7
+ export * from './global_variable_builder/index.js';
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,aAAa,CAAC;AAC5B,cAAc,sBAAsB,CAAC;AACrC,cAAc,wCAAwC,CAAC;AACvD,cAAc,oBAAoB,CAAC;AACnC,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,+BAA+B,EAAE,MAAM,sBAAsB,CAAC;AAIlG,cAAc,oCAAoC,CAAC"}
package/dest/index.js ADDED
@@ -0,0 +1,9 @@
1
+ export * from './client/index.js';
2
+ export * from './config.js';
3
+ export * from './publisher/index.js';
4
+ export * from './tx_validator/tx_validator_factory.js';
5
+ export * from './slasher/index.js';
6
+ export { Sequencer, SequencerState, getDefaultAllowedSetupFunctions } from './sequencer/index.js';
7
+ // Used by the node to simulate public parts of transactions. Should these be moved to a shared library?
8
+ // ISSUE(#9832)
9
+ export * from './global_variable_builder/index.js';
@@ -0,0 +1,31 @@
1
+ import { type BlobSinkConfig } from '@aztec/blob-sink/client';
2
+ import { type L1ReaderConfig, type L1TxUtilsConfig } from '@aztec/ethereum';
3
+ import { type ConfigMappingsType } from '@aztec/foundation/config';
4
+ import { EthAddress } from '@aztec/foundation/eth-address';
5
+ /**
6
+ * The configuration of the rollup transaction publisher.
7
+ */
8
+ export type TxSenderConfig = L1ReaderConfig & {
9
+ /**
10
+ * The private key to be used by the publisher.
11
+ */
12
+ publisherPrivateKey: `0x${string}`;
13
+ /**
14
+ * The address of the custom forwarder contract.
15
+ */
16
+ customForwarderContractAddress: EthAddress;
17
+ };
18
+ /**
19
+ * Configuration of the L1Publisher.
20
+ */
21
+ export type PublisherConfig = L1TxUtilsConfig & BlobSinkConfig & {
22
+ /**
23
+ * The interval to wait between publish retries.
24
+ */
25
+ l1PublishRetryIntervalMS: number;
26
+ };
27
+ export declare const getTxSenderConfigMappings: (scope: 'PROVER' | 'SEQ') => ConfigMappingsType<Omit<TxSenderConfig, 'l1Contracts'>>;
28
+ export declare function getTxSenderConfigFromEnv(scope: 'PROVER' | 'SEQ'): Omit<TxSenderConfig, 'l1Contracts'>;
29
+ export declare const getPublisherConfigMappings: (scope: 'PROVER' | 'SEQ') => ConfigMappingsType<PublisherConfig & L1TxUtilsConfig>;
30
+ export declare function getPublisherConfigFromEnv(scope: 'PROVER' | 'SEQ'): PublisherConfig;
31
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/publisher/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,cAAc,EAAyB,MAAM,yBAAyB,CAAC;AACrF,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,eAAe,EAIrB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,KAAK,kBAAkB,EAAyB,MAAM,0BAA0B,CAAC;AAC1F,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAE3D;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,cAAc,GAAG;IAC5C;;OAEG;IACH,mBAAmB,EAAE,KAAK,MAAM,EAAE,CAAC;IAEnC;;OAEG;IACH,8BAA8B,EAAE,UAAU,CAAC;CAC5C,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,eAAe,GAC3C,cAAc,GAAG;IACf;;OAEG;IACH,wBAAwB,EAAE,MAAM,CAAC;CAClC,CAAC;AAEJ,eAAO,MAAM,yBAAyB,EAAE,CACtC,KAAK,EAAE,QAAQ,GAAG,KAAK,KACpB,kBAAkB,CAAC,IAAI,CAAC,cAAc,EAAE,aAAa,CAAC,CAczD,CAAC;AAEH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,QAAQ,GAAG,KAAK,GAAG,IAAI,CAAC,cAAc,EAAE,aAAa,CAAC,CAErG;AAED,eAAO,MAAM,0BAA0B,EAAE,CACvC,KAAK,EAAE,QAAQ,GAAG,KAAK,KACpB,kBAAkB,CAAC,eAAe,GAAG,eAAe,CASvD,CAAC;AAEH,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,QAAQ,GAAG,KAAK,GAAG,eAAe,CAElF"}