@aztec/sequencer-client 0.1.0-alpha11

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 (146) hide show
  1. package/.eslintrc.cjs +1 -0
  2. package/.tsbuildinfo +1 -0
  3. package/README.md +45 -0
  4. package/dest/block_builder/index.d.ts +19 -0
  5. package/dest/block_builder/index.d.ts.map +1 -0
  6. package/dest/block_builder/index.js +2 -0
  7. package/dest/block_builder/solo_block_builder.d.ts +76 -0
  8. package/dest/block_builder/solo_block_builder.d.ts.map +1 -0
  9. package/dest/block_builder/solo_block_builder.js +453 -0
  10. package/dest/block_builder/solo_block_builder.test.d.ts +3 -0
  11. package/dest/block_builder/solo_block_builder.test.d.ts.map +1 -0
  12. package/dest/block_builder/solo_block_builder.test.js +291 -0
  13. package/dest/block_builder/types.d.ts +12 -0
  14. package/dest/block_builder/types.d.ts.map +1 -0
  15. package/dest/block_builder/types.js +2 -0
  16. package/dest/client/index.d.ts +2 -0
  17. package/dest/client/index.d.ts.map +1 -0
  18. package/dest/client/index.js +2 -0
  19. package/dest/client/sequencer-client.d.ts +32 -0
  20. package/dest/client/sequencer-client.d.ts.map +1 -0
  21. package/dest/client/sequencer-client.js +47 -0
  22. package/dest/config.d.ts +12 -0
  23. package/dest/config.d.ts.map +1 -0
  24. package/dest/config.js +25 -0
  25. package/dest/global_variable_builder/config.d.ts +19 -0
  26. package/dest/global_variable_builder/config.d.ts.map +1 -0
  27. package/dest/global_variable_builder/config.js +2 -0
  28. package/dest/global_variable_builder/global_builder.d.ts +30 -0
  29. package/dest/global_variable_builder/global_builder.d.ts.map +1 -0
  30. package/dest/global_variable_builder/global_builder.js +24 -0
  31. package/dest/global_variable_builder/index.d.ts +11 -0
  32. package/dest/global_variable_builder/index.d.ts.map +1 -0
  33. package/dest/global_variable_builder/index.js +12 -0
  34. package/dest/global_variable_builder/viem-reader.d.ts +26 -0
  35. package/dest/global_variable_builder/viem-reader.d.ts.map +1 -0
  36. package/dest/global_variable_builder/viem-reader.js +43 -0
  37. package/dest/index.d.ts +11 -0
  38. package/dest/index.d.ts.map +1 -0
  39. package/dest/index.js +12 -0
  40. package/dest/mocks/tx.d.ts +10 -0
  41. package/dest/mocks/tx.d.ts.map +1 -0
  42. package/dest/mocks/tx.js +18 -0
  43. package/dest/mocks/verification_keys.d.ts +28 -0
  44. package/dest/mocks/verification_keys.d.ts.map +1 -0
  45. package/dest/mocks/verification_keys.js +14 -0
  46. package/dest/prover/empty.d.ts +41 -0
  47. package/dest/prover/empty.d.ts.map +1 -0
  48. package/dest/prover/empty.js +57 -0
  49. package/dest/prover/index.d.ts +17 -0
  50. package/dest/prover/index.d.ts.map +1 -0
  51. package/dest/prover/index.js +2 -0
  52. package/dest/publisher/config.d.ts +33 -0
  53. package/dest/publisher/config.d.ts.map +1 -0
  54. package/dest/publisher/config.js +2 -0
  55. package/dest/publisher/index.d.ts +10 -0
  56. package/dest/publisher/index.d.ts.map +1 -0
  57. package/dest/publisher/index.js +11 -0
  58. package/dest/publisher/l1-publisher.d.ts +99 -0
  59. package/dest/publisher/l1-publisher.d.ts.map +1 -0
  60. package/dest/publisher/l1-publisher.js +154 -0
  61. package/dest/publisher/l1-publisher.test.d.ts +2 -0
  62. package/dest/publisher/l1-publisher.test.d.ts.map +1 -0
  63. package/dest/publisher/l1-publisher.test.js +58 -0
  64. package/dest/publisher/viem-tx-sender.d.ts +42 -0
  65. package/dest/publisher/viem-tx-sender.d.ts.map +1 -0
  66. package/dest/publisher/viem-tx-sender.js +115 -0
  67. package/dest/receiver.d.ts +13 -0
  68. package/dest/receiver.d.ts.map +1 -0
  69. package/dest/receiver.js +2 -0
  70. package/dest/sequencer/config.d.ts +26 -0
  71. package/dest/sequencer/config.d.ts.map +1 -0
  72. package/dest/sequencer/config.js +2 -0
  73. package/dest/sequencer/index.d.ts +4 -0
  74. package/dest/sequencer/index.d.ts.map +1 -0
  75. package/dest/sequencer/index.js +4 -0
  76. package/dest/sequencer/processed_tx.d.ts +34 -0
  77. package/dest/sequencer/processed_tx.d.ts.map +1 -0
  78. package/dest/sequencer/processed_tx.js +39 -0
  79. package/dest/sequencer/public_processor.d.ts +68 -0
  80. package/dest/sequencer/public_processor.d.ts.map +1 -0
  81. package/dest/sequencer/public_processor.js +195 -0
  82. package/dest/sequencer/public_processor.test.d.ts +2 -0
  83. package/dest/sequencer/public_processor.test.d.ts.map +1 -0
  84. package/dest/sequencer/public_processor.test.js +159 -0
  85. package/dest/sequencer/sequencer.d.ts +133 -0
  86. package/dest/sequencer/sequencer.d.ts.map +1 -0
  87. package/dest/sequencer/sequencer.js +293 -0
  88. package/dest/sequencer/sequencer.test.d.ts +2 -0
  89. package/dest/sequencer/sequencer.test.d.ts.map +1 -0
  90. package/dest/sequencer/sequencer.test.js +100 -0
  91. package/dest/sequencer/utils.d.ts +7 -0
  92. package/dest/sequencer/utils.d.ts.map +1 -0
  93. package/dest/sequencer/utils.js +9 -0
  94. package/dest/simulator/index.d.ts +42 -0
  95. package/dest/simulator/index.d.ts.map +1 -0
  96. package/dest/simulator/index.js +2 -0
  97. package/dest/simulator/public_executor.d.ts +39 -0
  98. package/dest/simulator/public_executor.d.ts.map +1 -0
  99. package/dest/simulator/public_executor.js +118 -0
  100. package/dest/simulator/public_kernel.d.ts +20 -0
  101. package/dest/simulator/public_kernel.d.ts.map +1 -0
  102. package/dest/simulator/public_kernel.js +27 -0
  103. package/dest/simulator/rollup.d.ts +33 -0
  104. package/dest/simulator/rollup.d.ts.map +1 -0
  105. package/dest/simulator/rollup.js +41 -0
  106. package/dest/utils.d.ts +12 -0
  107. package/dest/utils.d.ts.map +1 -0
  108. package/dest/utils.js +16 -0
  109. package/jest.integration.config.json +13 -0
  110. package/package.json +23 -0
  111. package/src/block_builder/index.ts +23 -0
  112. package/src/block_builder/solo_block_builder.test.ts +425 -0
  113. package/src/block_builder/solo_block_builder.ts +710 -0
  114. package/src/block_builder/types.ts +16 -0
  115. package/src/client/index.ts +1 -0
  116. package/src/client/sequencer-client.ts +79 -0
  117. package/src/config.ts +48 -0
  118. package/src/global_variable_builder/config.ts +19 -0
  119. package/src/global_variable_builder/global_builder.ts +43 -0
  120. package/src/global_variable_builder/index.ts +15 -0
  121. package/src/global_variable_builder/viem-reader.ts +63 -0
  122. package/src/index.ts +12 -0
  123. package/src/mocks/tx.ts +26 -0
  124. package/src/mocks/verification_keys.ts +36 -0
  125. package/src/prover/empty.ts +73 -0
  126. package/src/prover/index.ts +27 -0
  127. package/src/publisher/config.ts +36 -0
  128. package/src/publisher/index.ts +14 -0
  129. package/src/publisher/l1-publisher.test.ts +77 -0
  130. package/src/publisher/l1-publisher.ts +236 -0
  131. package/src/publisher/viem-tx-sender.ts +166 -0
  132. package/src/receiver.ts +13 -0
  133. package/src/sequencer/config.ts +27 -0
  134. package/src/sequencer/index.ts +3 -0
  135. package/src/sequencer/processed_tx.ts +82 -0
  136. package/src/sequencer/public_processor.test.ts +225 -0
  137. package/src/sequencer/public_processor.ts +280 -0
  138. package/src/sequencer/sequencer.test.ts +158 -0
  139. package/src/sequencer/sequencer.ts +356 -0
  140. package/src/sequencer/utils.ts +18 -0
  141. package/src/simulator/index.ts +51 -0
  142. package/src/simulator/public_executor.ts +137 -0
  143. package/src/simulator/public_kernel.ts +27 -0
  144. package/src/simulator/rollup.ts +54 -0
  145. package/src/utils.ts +16 -0
  146. package/tsconfig.json +38 -0
@@ -0,0 +1,356 @@
1
+ import { Fr } from '@aztec/foundation/fields';
2
+ import { createDebugLogger } from '@aztec/foundation/log';
3
+ import { RunningPromise } from '@aztec/foundation/running-promise';
4
+ import { P2P } from '@aztec/p2p';
5
+ import {
6
+ ContractData,
7
+ ContractPublicData,
8
+ L1ToL2MessageSource,
9
+ L2Block,
10
+ L2BlockSource,
11
+ MerkleTreeId,
12
+ Tx,
13
+ } from '@aztec/types';
14
+ import { WorldStateStatus, WorldStateSynchroniser } from '@aztec/world-state';
15
+ import times from 'lodash.times';
16
+ import { BlockBuilder } from '../block_builder/index.js';
17
+ import { L1Publisher } from '../publisher/l1-publisher.js';
18
+ import { ceilPowerOfTwo } from '../utils.js';
19
+ import { SequencerConfig } from './config.js';
20
+ import { ProcessedTx } from './processed_tx.js';
21
+ import { PublicProcessorFactory } from './public_processor.js';
22
+ import { GlobalVariables } from '@aztec/circuits.js';
23
+ import { GlobalVariableBuilder } from '../global_variable_builder/global_builder.js';
24
+
25
+ /**
26
+ * Sequencer client
27
+ * - Wins a period of time to become the sequencer (depending on finalised protocol).
28
+ * - Chooses a set of txs from the tx pool to be in the rollup.
29
+ * - Simulate the rollup of txs.
30
+ * - Adds proof requests to the request pool (not for this milestone).
31
+ * - Receives results to those proofs from the network (repeats as necessary) (not for this milestone).
32
+ * - Publishes L1 tx(s) to the rollup contract via RollupPublisher.
33
+ */
34
+ export class Sequencer {
35
+ private runningPromise?: RunningPromise;
36
+ private pollingIntervalMs: number;
37
+ private maxTxsPerBlock = 32;
38
+ private minTxsPerBLock = 1;
39
+ private lastPublishedBlock = 0;
40
+ private state = SequencerState.STOPPED;
41
+ private chainId: Fr;
42
+ private version: Fr;
43
+
44
+ constructor(
45
+ private publisher: L1Publisher,
46
+ private globalsBuilder: GlobalVariableBuilder,
47
+ private p2pClient: P2P,
48
+ private worldState: WorldStateSynchroniser,
49
+ private blockBuilder: BlockBuilder,
50
+ private l2BlockSource: L2BlockSource,
51
+ private l1ToL2MessageSource: L1ToL2MessageSource,
52
+ private publicProcessorFactory: PublicProcessorFactory,
53
+ config: SequencerConfig,
54
+ private log = createDebugLogger('aztec:sequencer'),
55
+ ) {
56
+ this.pollingIntervalMs = config.transactionPollingInterval ?? 1_000;
57
+ if (config.maxTxsPerBlock) {
58
+ this.maxTxsPerBlock = config.maxTxsPerBlock;
59
+ }
60
+ if (config.minTxsPerBlock) {
61
+ this.minTxsPerBLock = config.minTxsPerBlock;
62
+ }
63
+ this.chainId = new Fr(config.chainId);
64
+ this.version = new Fr(config.version);
65
+ }
66
+
67
+ /**
68
+ * Starts the sequencer and moves to IDLE state. Blocks until the initial sync is complete.
69
+ */
70
+ public async start() {
71
+ await this.initialSync();
72
+
73
+ this.runningPromise = new RunningPromise(this.work.bind(this), this.pollingIntervalMs);
74
+ this.runningPromise.start();
75
+ this.state = SequencerState.IDLE;
76
+ this.log('Sequencer started');
77
+ }
78
+
79
+ /**
80
+ * Stops the sequencer from processing txs and moves to STOPPED state.
81
+ */
82
+ public async stop(): Promise<void> {
83
+ await this.runningPromise?.stop();
84
+ this.publisher.interrupt();
85
+ this.state = SequencerState.STOPPED;
86
+ this.log('Stopped sequencer');
87
+ }
88
+
89
+ /**
90
+ * Starts a previously stopped sequencer.
91
+ */
92
+ public restart() {
93
+ this.log('Restarting sequencer');
94
+ this.runningPromise!.start();
95
+ this.state = SequencerState.IDLE;
96
+ }
97
+
98
+ /**
99
+ * Returns the current state of the sequencer.
100
+ * @returns An object with a state entry with one of SequencerState.
101
+ */
102
+ public status() {
103
+ return { state: this.state };
104
+ }
105
+
106
+ protected async initialSync() {
107
+ // TODO: Should we wait for worldstate to be ready, or is the caller expected to run await start?
108
+ this.lastPublishedBlock = await this.worldState.status().then((s: WorldStateStatus) => s.syncedToL2Block);
109
+ }
110
+
111
+ /**
112
+ * Grabs up to maxTxsPerBlock from the p2p client, constructs a block, and pushes it to L1.
113
+ */
114
+ protected async work() {
115
+ try {
116
+ // Update state when the previous block has been synced
117
+ const prevBlockSynced = await this.isBlockSynced();
118
+ if (prevBlockSynced && this.state === SequencerState.PUBLISHING_BLOCK) {
119
+ this.log(`Block has been synced`);
120
+ this.state = SequencerState.IDLE;
121
+ }
122
+
123
+ // Do not go forward with new block if the previous one has not been mined and processed
124
+ if (!prevBlockSynced) return;
125
+
126
+ this.state = SequencerState.WAITING_FOR_TXS;
127
+
128
+ // Get txs to build the new block
129
+ const pendingTxs = await this.p2pClient.getTxs();
130
+ if (pendingTxs.length < this.minTxsPerBLock) return;
131
+
132
+ // Filter out invalid txs
133
+ const validTxs = await this.takeValidTxs(pendingTxs);
134
+ if (validTxs.length < this.minTxsPerBLock) {
135
+ return;
136
+ }
137
+
138
+ this.log(`Processing ${validTxs.length} txs...`);
139
+ this.state = SequencerState.CREATING_BLOCK;
140
+
141
+ const blockNumber = (await this.l2BlockSource.getBlockHeight()) + 1;
142
+ const globalVariables = await this.globalsBuilder.buildGlobalVariables(new Fr(blockNumber));
143
+
144
+ // Process txs and drop the ones that fail processing
145
+ // We create a fresh processor each time to reset any cached state (eg storage writes)
146
+ const processor = this.publicProcessorFactory.create();
147
+ const [processedTxs, failedTxs] = await processor.process(validTxs, globalVariables);
148
+ if (failedTxs.length > 0) {
149
+ this.log(`Dropping failed txs ${(await Tx.getHashes(failedTxs)).join(', ')}`);
150
+ await this.p2pClient.deleteTxs(await Tx.getHashes(failedTxs));
151
+ }
152
+
153
+ if (processedTxs.length === 0) {
154
+ this.log('No txs processed correctly to build block. Exiting');
155
+ return;
156
+ }
157
+
158
+ // Get l1 to l2 messages from the contract
159
+ this.log('Requesting L1 to L2 messages from contract');
160
+ const l1ToL2Messages = await this.getPendingL1ToL2Messages();
161
+ this.log('Successfully retrieved L1 to L2 messages from contract');
162
+
163
+ // Build the new block by running the rollup circuits
164
+ this.log(`Assembling block with txs ${processedTxs.map(tx => tx.hash).join(', ')}`);
165
+ const emptyTx = await processor.makeEmptyProcessedTx(this.chainId, this.version);
166
+
167
+ const block = await this.buildBlock(processedTxs, l1ToL2Messages, emptyTx, globalVariables);
168
+ this.log(`Assembled block ${block.number}`);
169
+
170
+ await this.publishContractPublicData(validTxs, block);
171
+
172
+ await this.publishL2Block(block);
173
+ } catch (err) {
174
+ this.log(err);
175
+ this.log(`Rolling back world state DB`);
176
+ await this.worldState.getLatest().rollback();
177
+ }
178
+ }
179
+
180
+ /**
181
+ * Gets new contract public data from the txs and publishes it on chain.
182
+ * @param validTxs - The set of real transactions being published as part of the block.
183
+ * @param block - The L2Block to be published.
184
+ */
185
+ protected async publishContractPublicData(validTxs: Tx[], block: L2Block) {
186
+ // Publishes contract data for txs to the network and awaits the tx to be mined
187
+ this.state = SequencerState.PUBLISHING_CONTRACT_DATA;
188
+ const newContractData = validTxs
189
+ .map(tx => {
190
+ // Currently can only have 1 new contract per tx
191
+ const newContract = tx.data?.end.newContracts[0];
192
+ if (newContract && tx.newContractPublicFunctions?.length) {
193
+ return new ContractPublicData(
194
+ new ContractData(newContract.contractAddress, newContract.portalContractAddress),
195
+ tx.newContractPublicFunctions,
196
+ );
197
+ }
198
+ })
199
+ .filter((cd): cd is Exclude<typeof cd, undefined> => cd !== undefined);
200
+
201
+ const blockHash = block.getCalldataHash();
202
+ this.log(`Publishing contract public data with block hash ${blockHash.toString('hex')}`);
203
+
204
+ const publishedContractData = await this.publisher.processNewContractData(block.number, blockHash, newContractData);
205
+ if (publishedContractData) {
206
+ this.log(`Successfully published new contract data for block ${block.number}`);
207
+ } else if (!publishedContractData && newContractData.length) {
208
+ this.log(`Failed to publish new contract data for block ${block.number}`);
209
+ }
210
+ }
211
+
212
+ /**
213
+ * Publishes the L2Block to the rollup contract.
214
+ * @param block - The L2Block to be published.
215
+ */
216
+ protected async publishL2Block(block: L2Block) {
217
+ // Publishes new block to the network and awaits the tx to be mined
218
+ this.state = SequencerState.PUBLISHING_BLOCK;
219
+ const publishedL2Block = await this.publisher.processL2Block(block);
220
+ if (publishedL2Block) {
221
+ this.log(`Successfully published block ${block.number}`);
222
+ this.lastPublishedBlock = block.number;
223
+ } else {
224
+ throw new Error(`Failed to publish block`);
225
+ }
226
+ }
227
+
228
+ // TODO: It should be responsibility of the P2P layer to validate txs before passing them on here
229
+ protected async takeValidTxs(txs: Tx[]) {
230
+ const validTxs = [];
231
+ const doubleSpendTxs = [];
232
+
233
+ // Process txs until we get to maxTxsPerBlock, rejecting double spends in the process
234
+ for (const tx of txs) {
235
+ // TODO(AD) - eventually we should add a limit to how many transactions we
236
+ // skip in this manner and do something more DDOS-proof (like letting the transaction fail and pay a fee).
237
+ if (await this.isTxDoubleSpend(tx)) {
238
+ this.log(`Deleting double spend tx ${await tx.getTxHash()}`);
239
+ doubleSpendTxs.push(tx);
240
+ continue;
241
+ }
242
+
243
+ validTxs.push(tx);
244
+ if (validTxs.length >= this.maxTxsPerBlock) {
245
+ break;
246
+ }
247
+ }
248
+
249
+ // Make sure we remove these from the tx pool so we do not consider it again
250
+ if (doubleSpendTxs.length > 0) {
251
+ await this.p2pClient.deleteTxs(await Tx.getHashes([...doubleSpendTxs]));
252
+ }
253
+
254
+ return validTxs;
255
+ }
256
+
257
+ /**
258
+ * Returns whether the previous block sent has been mined, and all dependencies have caught up with it.
259
+ * @returns Boolean indicating if our dependencies are synced to the latest block.
260
+ */
261
+ protected async isBlockSynced() {
262
+ const syncedBlocks = await Promise.all([
263
+ this.worldState.status().then((s: WorldStateStatus) => s.syncedToL2Block),
264
+ this.p2pClient.getStatus().then(s => s.syncedToL2Block),
265
+ ]);
266
+ const min = Math.min(...syncedBlocks);
267
+ return min >= this.lastPublishedBlock;
268
+ }
269
+
270
+ /**
271
+ * Pads the set of txs to a power of two and assembles a block by calling the block builder.
272
+ * @param txs - Processed txs to include in the next block.
273
+ * @param newL1ToL2Messages - L1 to L2 messages to be part of the block.
274
+ * @param emptyTx - Empty tx to repeat at the end of the block to pad to a power of two.
275
+ * @param globalVariables - Global variables to use in the block.
276
+ * @returns The new block.
277
+ */
278
+ protected async buildBlock(
279
+ txs: ProcessedTx[],
280
+ newL1ToL2Messages: Fr[],
281
+ emptyTx: ProcessedTx,
282
+ globalVariables: GlobalVariables,
283
+ ) {
284
+ // Pad the txs array with empty txs to be a power of two, at least 4
285
+ const txsTargetSize = Math.max(ceilPowerOfTwo(txs.length), 4);
286
+ const emptyTxCount = txsTargetSize - txs.length;
287
+
288
+ const allTxs = [...txs, ...times(emptyTxCount, () => emptyTx)];
289
+ this.log(`Building block ${globalVariables.blockNumber}`);
290
+
291
+ const [block] = await this.blockBuilder.buildL2Block(globalVariables, allTxs, newL1ToL2Messages);
292
+ return block;
293
+ }
294
+
295
+ /**
296
+ * Calls the archiver to pull upto `NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP` message keys
297
+ * (archiver returns the top messages sorted by fees)
298
+ * @returns An array of L1 to L2 messages' messageKeys
299
+ */
300
+ protected async getPendingL1ToL2Messages(): Promise<Fr[]> {
301
+ return await this.l1ToL2MessageSource.getPendingL1ToL2Messages();
302
+ }
303
+
304
+ /**
305
+ * Returns true if one of the transaction nullifiers exist.
306
+ * Nullifiers prevent double spends in a private context.
307
+ * @param tx - The transaction.
308
+ * @returns Whether this is a problematic double spend that the L1 contract would reject.
309
+ */
310
+ protected async isTxDoubleSpend(tx: Tx): Promise<boolean> {
311
+ // eslint-disable-next-line @typescript-eslint/await-thenable
312
+ for (const nullifier of tx.data.end.newNullifiers) {
313
+ // Skip nullifier if it's empty
314
+ if (nullifier.isZero()) continue;
315
+ // TODO(AD): this is an exhaustive search currently
316
+ if (
317
+ (await this.worldState.getLatest().findLeafIndex(MerkleTreeId.NULLIFIER_TREE, nullifier.toBuffer())) !==
318
+ undefined
319
+ ) {
320
+ // Our nullifier tree has this nullifier already - this transaction is a double spend / not well-formed
321
+ return true;
322
+ }
323
+ }
324
+ return false;
325
+ }
326
+ }
327
+
328
+ /**
329
+ * State of the sequencer.
330
+ */
331
+ export enum SequencerState {
332
+ /**
333
+ * Will move to WAITING_FOR_TXS after a configured amount of time.
334
+ */
335
+ IDLE,
336
+ /**
337
+ * Polling the P2P module for txs to include in a block. Will move to CREATING_BLOCK if there are valid txs to include, or back to IDLE otherwise.
338
+ */
339
+ WAITING_FOR_TXS,
340
+ /**
341
+ * Creating a new L2 block. Includes processing public function calls and running rollup circuits. Will move to PUBLISHING_CONTRACT_DATA.
342
+ */
343
+ CREATING_BLOCK,
344
+ /**
345
+ * Sending the tx to L1 with encrypted logs and awaiting it to be mined. Will move back to PUBLISHING_BLOCK once finished.
346
+ */
347
+ PUBLISHING_CONTRACT_DATA,
348
+ /**
349
+ * Sending the tx to L1 with the L2 block data and awaiting it to be mined. Will move to IDLE.
350
+ */
351
+ PUBLISHING_BLOCK,
352
+ /**
353
+ * Sequencer is stopped and not processing any txs from the pool.
354
+ */
355
+ STOPPED,
356
+ }
@@ -0,0 +1,18 @@
1
+ import { CombinedHistoricTreeRoots, Fr, PrivateHistoricTreeRoots } from '@aztec/circuits.js';
2
+ import { MerkleTreeId } from '@aztec/types';
3
+ import { MerkleTreeOperations } from '@aztec/world-state';
4
+
5
+ /**
6
+ * Fetches the private, nullifier, contract tree and l1 to l2 messages tree roots from a given db and assembles a CombinedHistoricTreeRoots object.
7
+ */
8
+ export async function getCombinedHistoricTreeRoots(db: MerkleTreeOperations) {
9
+ return new CombinedHistoricTreeRoots(
10
+ new PrivateHistoricTreeRoots(
11
+ Fr.fromBuffer((await db.getTreeInfo(MerkleTreeId.PRIVATE_DATA_TREE)).root),
12
+ Fr.fromBuffer((await db.getTreeInfo(MerkleTreeId.NULLIFIER_TREE)).root),
13
+ Fr.fromBuffer((await db.getTreeInfo(MerkleTreeId.CONTRACT_TREE)).root),
14
+ Fr.fromBuffer((await db.getTreeInfo(MerkleTreeId.L1_TO_L2_MESSAGES_TREE)).root),
15
+ Fr.ZERO,
16
+ ),
17
+ );
18
+ }
@@ -0,0 +1,51 @@
1
+ import {
2
+ BaseOrMergeRollupPublicInputs,
3
+ BaseRollupInputs,
4
+ MergeRollupInputs,
5
+ PublicKernelInputs,
6
+ PublicKernelPublicInputs,
7
+ RootRollupInputs,
8
+ RootRollupPublicInputs,
9
+ } from '@aztec/circuits.js';
10
+
11
+ /**
12
+ * Circuit simulator for the rollup circuits.
13
+ */
14
+ export interface RollupSimulator {
15
+ /**
16
+ * Simulates the base rollup circuit from its inputs.
17
+ * @param input - Inputs to the circuit.
18
+ * @returns The public inputs as outputs of the simulation.
19
+ */
20
+ baseRollupCircuit(input: BaseRollupInputs): Promise<BaseOrMergeRollupPublicInputs>;
21
+ /**
22
+ * Simulates the merge rollup circuit from its inputs.
23
+ * @param input - Inputs to the circuit.
24
+ * @returns The public inputs as outputs of the simulation.
25
+ */
26
+ mergeRollupCircuit(input: MergeRollupInputs): Promise<BaseOrMergeRollupPublicInputs>;
27
+ /**
28
+ * Simulates the root rollup circuit from its inputs.
29
+ * @param input - Inputs to the circuit.
30
+ * @returns The public inputs as outputs of the simulation.
31
+ */
32
+ rootRollupCircuit(input: RootRollupInputs): Promise<RootRollupPublicInputs>;
33
+ }
34
+
35
+ /**
36
+ * Circuit simulator for the public kernel circuits.
37
+ */
38
+ export interface PublicKernelCircuitSimulator {
39
+ /**
40
+ * Simulates the public kernel circuit (with a previous private kernel circuit run) from its inputs.
41
+ * @param input - Inputs to the circuit.
42
+ * @returns The public inputs as outputs of the simulation.
43
+ */
44
+ publicKernelCircuitPrivateInput(inputs: PublicKernelInputs): Promise<PublicKernelPublicInputs>;
45
+ /**
46
+ * Simulates the public kernel circuit (with no previous public kernel circuit run) from its inputs.
47
+ * @param input - Inputs to the circuit.
48
+ * @returns The public inputs as outputs of the simulation.
49
+ */
50
+ publicKernelCircuitNonFirstIteration(inputs: PublicKernelInputs): Promise<PublicKernelPublicInputs>;
51
+ }
@@ -0,0 +1,137 @@
1
+ import {
2
+ CommitmentDataOracleInputs,
3
+ CommitmentsDB,
4
+ MessageLoadOracleInputs,
5
+ PublicContractsDB,
6
+ PublicExecutor,
7
+ PublicStateDB,
8
+ } from '@aztec/acir-simulator';
9
+ import { AztecAddress, CircuitsWasm, EthAddress, Fr, PrivateHistoricTreeRoots } from '@aztec/circuits.js';
10
+ import { siloCommitment } from '@aztec/circuits.js/abis';
11
+ import { ContractDataSource, L1ToL2MessageSource, MerkleTreeId } from '@aztec/types';
12
+ import { MerkleTreeOperations, computePublicDataTreeLeafIndex } from '@aztec/world-state';
13
+
14
+ /**
15
+ * Returns a new PublicExecutor simulator backed by the supplied merkle tree db and contract data source.
16
+ * @param merkleTree - A merkle tree database.
17
+ * @param contractDataSource - A contract data source.
18
+ * @returns A new instance of a PublicExecutor.
19
+ */
20
+ export function getPublicExecutor(
21
+ merkleTree: MerkleTreeOperations,
22
+ contractDataSource: ContractDataSource,
23
+ l1toL2MessageSource: L1ToL2MessageSource,
24
+ ) {
25
+ return new PublicExecutor(
26
+ new WorldStatePublicDB(merkleTree),
27
+ new ContractsDataSourcePublicDB(contractDataSource),
28
+ new WorldStateDB(merkleTree, l1toL2MessageSource),
29
+ );
30
+ }
31
+
32
+ /**
33
+ * Implements the PublicContractsDB using a ContractDataSource.
34
+ */
35
+ class ContractsDataSourcePublicDB implements PublicContractsDB {
36
+ constructor(private db: ContractDataSource) {}
37
+ async getBytecode(address: AztecAddress, functionSelector: Buffer): Promise<Buffer | undefined> {
38
+ return (await this.db.getPublicFunction(address, functionSelector))?.bytecode;
39
+ }
40
+ async getPortalContractAddress(address: AztecAddress): Promise<EthAddress | undefined> {
41
+ return (await this.db.getL2ContractInfo(address))?.portalContractAddress;
42
+ }
43
+ }
44
+
45
+ /**
46
+ * Implements the PublicStateDB using a world-state database.
47
+ */
48
+ class WorldStatePublicDB implements PublicStateDB {
49
+ private writeCache: Map<bigint, Fr> = new Map();
50
+
51
+ constructor(private db: MerkleTreeOperations) {}
52
+
53
+ /**
54
+ * Reads a value from public storage, returning zero if none.
55
+ * @param contract - Owner of the storage.
56
+ * @param slot - Slot to read in the contract storage.
57
+ * @returns The current value in the storage slot.
58
+ */
59
+ public async storageRead(contract: AztecAddress, slot: Fr): Promise<Fr> {
60
+ const index = computePublicDataTreeLeafIndex(contract, slot, await CircuitsWasm.get());
61
+ const cached = this.writeCache.get(index);
62
+ if (cached !== undefined) return cached;
63
+ const value = await this.db.getLeafValue(MerkleTreeId.PUBLIC_DATA_TREE, index);
64
+ return value ? Fr.fromBuffer(value) : Fr.ZERO;
65
+ }
66
+
67
+ /**
68
+ * Records a write to public storage.
69
+ * @param contract - Owner of the storage.
70
+ * @param slot - Slot to read in the contract storage.
71
+ * @param newValue - The new value to store.
72
+ */
73
+ public async storageWrite(contract: AztecAddress, slot: Fr, newValue: Fr): Promise<void> {
74
+ const index = computePublicDataTreeLeafIndex(contract, slot, await CircuitsWasm.get());
75
+ this.writeCache.set(index, newValue);
76
+ }
77
+ }
78
+
79
+ /**
80
+ * Implements WorldState db using a world state database.
81
+ */
82
+ export class WorldStateDB implements CommitmentsDB {
83
+ constructor(private db: MerkleTreeOperations, private l1ToL2MessageSource: L1ToL2MessageSource) {}
84
+
85
+ /**
86
+ * Gets a confirmed L1 to L2 message for the given message key.
87
+ * TODO(Maddiaa): Can be combined with aztec-node method that does the same thing.
88
+ * @param messageKey - The message Key.
89
+ * @returns - The l1 to l2 message object
90
+ */
91
+ public async getL1ToL2Message(messageKey: Fr): Promise<MessageLoadOracleInputs> {
92
+ // todo: #697 - make this one lookup.
93
+ const message = await this.l1ToL2MessageSource.getConfirmedL1ToL2Message(messageKey);
94
+ const index = (await this.db.findLeafIndex(MerkleTreeId.L1_TO_L2_MESSAGES_TREE, messageKey.toBuffer()))!;
95
+ const siblingPath = await this.db.getSiblingPath(MerkleTreeId.L1_TO_L2_MESSAGES_TREE, index);
96
+
97
+ return {
98
+ message: message.toFieldArray(),
99
+ index,
100
+ siblingPath: siblingPath.toFieldArray(),
101
+ };
102
+ }
103
+
104
+ /**
105
+ * Gets a message index and sibling path to some commitment in the private data tree.
106
+ * @param address - The contract address owning storage.
107
+ * @param commitment - The preimage of the siloed data.
108
+ * @returns - The Commitment data oracle object
109
+ */
110
+ public async getCommitmentOracle(address: AztecAddress, commitment: Fr): Promise<CommitmentDataOracleInputs> {
111
+ const siloedCommitment = siloCommitment(await CircuitsWasm.get(), address, commitment);
112
+ const index = (await this.db.findLeafIndex(MerkleTreeId.PRIVATE_DATA_TREE, siloedCommitment.toBuffer()))!;
113
+ const siblingPath = await this.db.getSiblingPath(MerkleTreeId.PRIVATE_DATA_TREE, index);
114
+
115
+ return {
116
+ commitment: siloedCommitment,
117
+ index,
118
+ siblingPath: siblingPath.toFieldArray(),
119
+ };
120
+ }
121
+
122
+ /**
123
+ * Gets the current tree roots from the merkle db.
124
+ * @returns current tree roots.
125
+ */
126
+ public getTreeRoots(): PrivateHistoricTreeRoots {
127
+ const roots = this.db.getCommitmentTreeRoots();
128
+
129
+ return PrivateHistoricTreeRoots.from({
130
+ privateKernelVkTreeRoot: Fr.ZERO,
131
+ privateDataTreeRoot: Fr.fromBuffer(roots.privateDataTreeRoot),
132
+ contractTreeRoot: Fr.fromBuffer(roots.contractDataTreeRoot),
133
+ nullifierTreeRoot: Fr.fromBuffer(roots.nullifierTreeRoot),
134
+ l1ToL2MessagesTreeRoot: Fr.fromBuffer(roots.l1Tol2MessagesTreeRoot),
135
+ });
136
+ }
137
+ }
@@ -0,0 +1,27 @@
1
+ import { PublicKernelInputs, PublicKernelPublicInputs, simulatePublicKernelCircuit } from '@aztec/circuits.js';
2
+ import { PublicKernelCircuitSimulator } from './index.js';
3
+
4
+ /**
5
+ * Implements the PublicKernelCircuitSimulator by calling the wasm implementations of the circuits.
6
+ */
7
+ export class WasmPublicKernelCircuitSimulator implements PublicKernelCircuitSimulator {
8
+ /**
9
+ * Simulates the public kernel circuit (with a previous private kernel circuit run) from its inputs.
10
+ * @param input - Inputs to the circuit.
11
+ * @returns The public inputs as outputs of the simulation.
12
+ */
13
+ public publicKernelCircuitPrivateInput(input: PublicKernelInputs): Promise<PublicKernelPublicInputs> {
14
+ if (!input.previousKernel.publicInputs.isPrivate) throw new Error(`Expected private kernel previous inputs`);
15
+ return simulatePublicKernelCircuit(input);
16
+ }
17
+
18
+ /**
19
+ * Simulates the public kernel circuit (with no previous public kernel circuit run) from its inputs.
20
+ * @param input - Inputs to the circuit.
21
+ * @returns The public inputs as outputs of the simulation.
22
+ */
23
+ publicKernelCircuitNonFirstIteration(input: PublicKernelInputs): Promise<PublicKernelPublicInputs> {
24
+ if (input.previousKernel.publicInputs.isPrivate) throw new Error(`Expected public kernel previous inputs`);
25
+ return simulatePublicKernelCircuit(input);
26
+ }
27
+ }
@@ -0,0 +1,54 @@
1
+ import {
2
+ BaseOrMergeRollupPublicInputs,
3
+ BaseRollupInputs,
4
+ CircuitsWasm,
5
+ MergeRollupInputs,
6
+ RollupWasmWrapper,
7
+ RootRollupInputs,
8
+ RootRollupPublicInputs,
9
+ } from '@aztec/circuits.js';
10
+ import { RollupSimulator } from './index.js';
11
+
12
+ /**
13
+ * Implements the rollup circuit simulator using the wasm circuits implementation.
14
+ */
15
+ export class WasmRollupCircuitSimulator implements RollupSimulator {
16
+ private rollupWasmWrapper: RollupWasmWrapper;
17
+
18
+ constructor(wasm: CircuitsWasm) {
19
+ this.rollupWasmWrapper = new RollupWasmWrapper(wasm);
20
+ }
21
+
22
+ /**
23
+ * Creates a new instance using the default CircuitsWasm module.
24
+ * @returns A new instance.
25
+ */
26
+ public static async new() {
27
+ return new this(await CircuitsWasm.get());
28
+ }
29
+
30
+ /**
31
+ * Simulates the base rollup circuit from its inputs.
32
+ * @param input - Inputs to the circuit.
33
+ * @returns The public inputs as outputs of the simulation.
34
+ */
35
+ baseRollupCircuit(input: BaseRollupInputs): Promise<BaseOrMergeRollupPublicInputs> {
36
+ return Promise.resolve(this.rollupWasmWrapper.simulateBaseRollup(input));
37
+ }
38
+ /**
39
+ * Simulates the merge rollup circuit from its inputs.
40
+ * @param input - Inputs to the circuit.
41
+ * @returns The public inputs as outputs of the simulation.
42
+ */
43
+ mergeRollupCircuit(input: MergeRollupInputs): Promise<BaseOrMergeRollupPublicInputs> {
44
+ return Promise.resolve(this.rollupWasmWrapper.simulateMergeRollup(input));
45
+ }
46
+ /**
47
+ * Simulates the root rollup circuit from its inputs.
48
+ * @param input - Inputs to the circuit.
49
+ * @returns The public inputs as outputs of the simulation.
50
+ */
51
+ rootRollupCircuit(input: RootRollupInputs): Promise<RootRollupPublicInputs> {
52
+ return Promise.resolve(this.rollupWasmWrapper.simulateRootRollup(input));
53
+ }
54
+ }
package/src/utils.ts ADDED
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Returns a promise that resolves after ms milliseconds, returning retval.
3
+ * @param ms - How many milliseconds to sleep.
4
+ * @param retval - The return value of the promise.
5
+ */
6
+ export function sleep<T>(ms: number, retval: T): Promise<T> {
7
+ return new Promise(resolve => setTimeout(() => resolve(retval), ms));
8
+ }
9
+
10
+ /**
11
+ * Returns the lowest power of two that is greater of equal to the input.
12
+ * @param num - The input.
13
+ */
14
+ export function ceilPowerOfTwo(num: number): number {
15
+ return 2 ** Math.ceil(Math.log2(num));
16
+ }