@aztec/sequencer-client 0.16.4 → 0.16.6

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 (37) hide show
  1. package/dest/block_builder/solo_block_builder.d.ts +3 -3
  2. package/dest/block_builder/solo_block_builder.d.ts.map +1 -1
  3. package/dest/block_builder/solo_block_builder.js +26 -26
  4. package/dest/block_builder/types.d.ts +11 -2
  5. package/dest/block_builder/types.d.ts.map +1 -1
  6. package/dest/sequencer/utils.js +2 -2
  7. package/package.json +11 -11
  8. package/src/block_builder/index.ts +0 -24
  9. package/src/block_builder/solo_block_builder.ts +0 -758
  10. package/src/block_builder/types.ts +0 -16
  11. package/src/client/index.ts +0 -1
  12. package/src/client/sequencer-client.ts +0 -87
  13. package/src/config.ts +0 -76
  14. package/src/global_variable_builder/config.ts +0 -20
  15. package/src/global_variable_builder/global_builder.ts +0 -87
  16. package/src/global_variable_builder/index.ts +0 -16
  17. package/src/global_variable_builder/viem-reader.ts +0 -61
  18. package/src/index.ts +0 -15
  19. package/src/mocks/verification_keys.ts +0 -36
  20. package/src/prover/empty.ts +0 -74
  21. package/src/prover/index.ts +0 -53
  22. package/src/publisher/config.ts +0 -41
  23. package/src/publisher/index.ts +0 -14
  24. package/src/publisher/l1-publisher.ts +0 -291
  25. package/src/publisher/viem-tx-sender.ts +0 -195
  26. package/src/receiver.ts +0 -13
  27. package/src/sequencer/config.ts +0 -1
  28. package/src/sequencer/index.ts +0 -3
  29. package/src/sequencer/processed_tx.ts +0 -107
  30. package/src/sequencer/public_processor.ts +0 -433
  31. package/src/sequencer/sequencer.ts +0 -434
  32. package/src/sequencer/utils.ts +0 -25
  33. package/src/simulator/index.ts +0 -51
  34. package/src/simulator/public_executor.ts +0 -153
  35. package/src/simulator/public_kernel.ts +0 -54
  36. package/src/simulator/rollup.ts +0 -76
  37. package/src/utils.ts +0 -16
@@ -1,758 +0,0 @@
1
- import {
2
- AppendOnlyTreeSnapshot,
3
- BLOCKS_TREE_HEIGHT,
4
- BaseOrMergeRollupPublicInputs,
5
- BaseRollupInputs,
6
- CONTRACT_SUBTREE_HEIGHT,
7
- CONTRACT_SUBTREE_SIBLING_PATH_LENGTH,
8
- ConstantRollupData,
9
- GlobalVariables,
10
- L1_TO_L2_MSG_SUBTREE_HEIGHT,
11
- L1_TO_L2_MSG_SUBTREE_SIBLING_PATH_LENGTH,
12
- MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP,
13
- MAX_PUBLIC_DATA_READS_PER_BASE_ROLLUP,
14
- MAX_PUBLIC_DATA_READS_PER_TX,
15
- MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_BASE_ROLLUP,
16
- MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX,
17
- MembershipWitness,
18
- MergeRollupInputs,
19
- NOTE_HASH_SUBTREE_HEIGHT,
20
- NOTE_HASH_SUBTREE_SIBLING_PATH_LENGTH,
21
- NULLIFIER_SUBTREE_HEIGHT,
22
- NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH,
23
- NULLIFIER_TREE_HEIGHT,
24
- NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP,
25
- NullifierLeafPreimage,
26
- PUBLIC_DATA_TREE_HEIGHT,
27
- PreviousKernelData,
28
- PreviousRollupData,
29
- Proof,
30
- ROLLUP_VK_TREE_HEIGHT,
31
- RollupTypes,
32
- RootRollupInputs,
33
- RootRollupPublicInputs,
34
- VK_TREE_HEIGHT,
35
- VerificationKey,
36
- makeTuple,
37
- } from '@aztec/circuits.js';
38
- import {
39
- computeBlockHash,
40
- computeBlockHashWithGlobals,
41
- computeContractLeaf,
42
- computeGlobalsHash,
43
- } from '@aztec/circuits.js/abis';
44
- import { toFriendlyJSON } from '@aztec/circuits.js/utils';
45
- import { toBigIntBE } from '@aztec/foundation/bigint-buffer';
46
- import { padArrayEnd } from '@aztec/foundation/collection';
47
- import { Fr } from '@aztec/foundation/fields';
48
- import { createDebugLogger } from '@aztec/foundation/log';
49
- import { Tuple, assertLength } from '@aztec/foundation/serialize';
50
- import { ContractData, L2Block, L2BlockL2Logs, MerkleTreeId, PublicDataWrite, TxL2Logs } from '@aztec/types';
51
- import { MerkleTreeOperations } from '@aztec/world-state';
52
-
53
- import chunk from 'lodash.chunk';
54
- import flatMap from 'lodash.flatmap';
55
-
56
- import { VerificationKeys } from '../mocks/verification_keys.js';
57
- import { RollupProver } from '../prover/index.js';
58
- import { ProcessedTx } from '../sequencer/processed_tx.js';
59
- import { RollupSimulator } from '../simulator/index.js';
60
- import { BlockBuilder } from './index.js';
61
- import { AllowedTreeNames, OutputWithTreeSnapshot } from './types.js';
62
-
63
- const frToBigInt = (fr: Fr) => toBigIntBE(fr.toBuffer());
64
-
65
- // Denotes fields that are not used now, but will be in the future
66
- const FUTURE_FR = new Fr(0n);
67
- const FUTURE_NUM = 0;
68
-
69
- // Denotes fields that should be deleted
70
- const DELETE_FR = new Fr(0n);
71
-
72
- /**
73
- * Builds an L2 block out of a set of ProcessedTx's,
74
- * using the base, merge, and root rollup circuits.
75
- */
76
- export class SoloBlockBuilder implements BlockBuilder {
77
- constructor(
78
- protected db: MerkleTreeOperations,
79
- protected vks: VerificationKeys,
80
- protected simulator: RollupSimulator,
81
- protected prover: RollupProver,
82
- protected debug = createDebugLogger('aztec:sequencer:solo-block-builder'),
83
- ) {}
84
-
85
- /**
86
- * Builds an L2 block with the given number containing the given txs, updating state trees.
87
- * @param globalVariables - Global variables to be used in the block.
88
- * @param txs - Processed transactions to include in the block.
89
- * @param newL1ToL2Messages - L1 to L2 messages to be part of the block.
90
- * @param timestamp - Timestamp of the block.
91
- * @returns The new L2 block and a correctness proof as returned by the root rollup circuit.
92
- */
93
- public async buildL2Block(
94
- globalVariables: GlobalVariables,
95
- txs: ProcessedTx[],
96
- newL1ToL2Messages: Fr[],
97
- ): Promise<[L2Block, Proof]> {
98
- const [
99
- startNoteHashTreeSnapshot,
100
- startNullifierTreeSnapshot,
101
- startContractTreeSnapshot,
102
- startPublicDataTreeSnapshot,
103
- startL1ToL2MessageTreeSnapshot,
104
- startBlocksTreeSnapshot,
105
- ] = await Promise.all(
106
- [
107
- MerkleTreeId.NOTE_HASH_TREE,
108
- MerkleTreeId.NULLIFIER_TREE,
109
- MerkleTreeId.CONTRACT_TREE,
110
- MerkleTreeId.PUBLIC_DATA_TREE,
111
- MerkleTreeId.L1_TO_L2_MESSAGES_TREE,
112
- MerkleTreeId.BLOCKS_TREE,
113
- ].map(tree => this.getTreeSnapshot(tree)),
114
- );
115
-
116
- // Check txs are good for processing
117
- this.validateTxs(txs);
118
-
119
- // We fill the tx batch with empty txs, we process only one tx at a time for now
120
- const [circuitsOutput, proof] = await this.runCircuits(globalVariables, txs, newL1ToL2Messages);
121
-
122
- const {
123
- endNoteHashTreeSnapshot,
124
- endNullifierTreeSnapshot,
125
- endContractTreeSnapshot,
126
- endPublicDataTreeRoot,
127
- endL1ToL2MessagesTreeSnapshot,
128
- endBlocksTreeSnapshot,
129
- } = circuitsOutput;
130
-
131
- // Collect all new nullifiers, commitments, and contracts from all txs in this block
132
- const newNullifiers = flatMap(txs, tx => tx.data.end.newNullifiers);
133
- const newCommitments = flatMap(txs, tx => tx.data.end.newCommitments);
134
- const newContracts = flatMap(txs, tx => tx.data.end.newContracts).map(cd => computeContractLeaf(cd));
135
- const newContractData = flatMap(txs, tx => tx.data.end.newContracts).map(
136
- n => new ContractData(n.contractAddress, n.portalContractAddress),
137
- );
138
- const newPublicDataWrites = flatMap(txs, tx =>
139
- tx.data.end.publicDataUpdateRequests.map(t => new PublicDataWrite(t.leafIndex, t.newValue)),
140
- );
141
- const newL2ToL1Msgs = flatMap(txs, tx => tx.data.end.newL2ToL1Msgs);
142
-
143
- // Consolidate logs data from all txs
144
- const encryptedLogsArr: TxL2Logs[] = [];
145
- const unencryptedLogsArr: TxL2Logs[] = [];
146
- for (const tx of txs) {
147
- const encryptedLogs = tx.encryptedLogs || new TxL2Logs([]);
148
- encryptedLogsArr.push(encryptedLogs);
149
- const unencryptedLogs = tx.unencryptedLogs || new TxL2Logs([]);
150
- unencryptedLogsArr.push(unencryptedLogs);
151
- }
152
- const newEncryptedLogs = new L2BlockL2Logs(encryptedLogsArr);
153
- const newUnencryptedLogs = new L2BlockL2Logs(unencryptedLogsArr);
154
-
155
- const l2Block = L2Block.fromFields({
156
- number: Number(globalVariables.blockNumber.value),
157
- globalVariables,
158
- startNoteHashTreeSnapshot,
159
- endNoteHashTreeSnapshot,
160
- startNullifierTreeSnapshot,
161
- endNullifierTreeSnapshot,
162
- startContractTreeSnapshot,
163
- endContractTreeSnapshot,
164
- startPublicDataTreeRoot: startPublicDataTreeSnapshot.root,
165
- endPublicDataTreeRoot,
166
- startL1ToL2MessagesTreeSnapshot: startL1ToL2MessageTreeSnapshot,
167
- endL1ToL2MessagesTreeSnapshot,
168
- startBlocksTreeSnapshot,
169
- endBlocksTreeSnapshot,
170
- newCommitments,
171
- newNullifiers,
172
- newL2ToL1Msgs,
173
- newContracts,
174
- newContractData,
175
- newPublicDataWrites,
176
- newL1ToL2Messages,
177
- newEncryptedLogs,
178
- newUnencryptedLogs,
179
- });
180
-
181
- if (!l2Block.getCalldataHash().equals(circuitsOutput.sha256CalldataHash())) {
182
- throw new Error(
183
- `Calldata hash mismatch, ${l2Block.getCalldataHash().toString('hex')} == ${circuitsOutput
184
- .sha256CalldataHash()
185
- .toString('hex')} `,
186
- );
187
- }
188
-
189
- return [l2Block, proof];
190
- }
191
-
192
- protected validateTxs(txs: ProcessedTx[]) {
193
- for (const tx of txs) {
194
- for (const historicalTreeRoot of [
195
- 'noteHashTreeRoot',
196
- 'contractTreeRoot',
197
- 'nullifierTreeRoot',
198
- 'l1ToL2MessagesTreeRoot',
199
- ] as const) {
200
- if (tx.data.constants.blockHeader[historicalTreeRoot].isZero()) {
201
- throw new Error(`Empty ${historicalTreeRoot} for tx: ${toFriendlyJSON(tx)}`);
202
- }
203
- }
204
- }
205
- }
206
-
207
- protected async getTreeSnapshot(id: MerkleTreeId): Promise<AppendOnlyTreeSnapshot> {
208
- const treeInfo = await this.db.getTreeInfo(id);
209
- return new AppendOnlyTreeSnapshot(Fr.fromBuffer(treeInfo.root), Number(treeInfo.size));
210
- }
211
-
212
- protected async runCircuits(
213
- globalVariables: GlobalVariables,
214
- txs: ProcessedTx[],
215
- newL1ToL2Messages: Fr[],
216
- ): Promise<[RootRollupPublicInputs, Proof]> {
217
- // Check that the length of the array of txs is a power of two
218
- // See https://graphics.stanford.edu/~seander/bithacks.html#DetermineIfPowerOf2
219
- if (txs.length < 4 || (txs.length & (txs.length - 1)) !== 0) {
220
- throw new Error(`Length of txs for the block should be a power of two and at least four (got ${txs.length})`);
221
- }
222
-
223
- // padArrayEnd throws if the array is already full. Otherwise it pads till we reach the required size
224
- const newL1ToL2MessagesTuple = padArrayEnd(newL1ToL2Messages, Fr.ZERO, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
225
-
226
- // Run the base rollup circuits for the txs
227
- const baseRollupOutputs: [BaseOrMergeRollupPublicInputs, Proof][] = [];
228
- for (const pair of chunk(txs, 2)) {
229
- const [tx1, tx2] = pair;
230
- baseRollupOutputs.push(await this.baseRollupCircuit(tx1, tx2, globalVariables));
231
- }
232
-
233
- // Run merge rollups in layers until we have only two outputs
234
- let mergeRollupInputs: [BaseOrMergeRollupPublicInputs, Proof][] = baseRollupOutputs;
235
- let mergeRollupOutputs: [BaseOrMergeRollupPublicInputs, Proof][] = [];
236
- while (mergeRollupInputs.length > 2) {
237
- for (const pair of chunk(mergeRollupInputs, 2)) {
238
- const [r1, r2] = pair;
239
- mergeRollupOutputs.push(await this.mergeRollupCircuit(r1, r2));
240
- }
241
- mergeRollupInputs = mergeRollupOutputs;
242
- mergeRollupOutputs = [];
243
- }
244
-
245
- // Run the root rollup with the last two merge rollups (or base, if no merge layers)
246
- const [mergeOutputLeft, mergeOutputRight] = mergeRollupInputs;
247
- return this.rootRollupCircuit(mergeOutputLeft, mergeOutputRight, newL1ToL2MessagesTuple);
248
- }
249
-
250
- protected async baseRollupCircuit(
251
- tx1: ProcessedTx,
252
- tx2: ProcessedTx,
253
- globalVariables: GlobalVariables,
254
- ): Promise<[BaseOrMergeRollupPublicInputs, Proof]> {
255
- this.debug(`Running base rollup for ${tx1.hash} ${tx2.hash}`);
256
- const rollupInput = await this.buildBaseRollupInput(tx1, tx2, globalVariables);
257
- const rollupOutput = await this.simulator.baseRollupCircuit(rollupInput);
258
- await this.validateTrees(rollupOutput);
259
- const proof = await this.prover.getBaseRollupProof(rollupInput, rollupOutput);
260
- return [rollupOutput, proof];
261
- }
262
-
263
- protected async mergeRollupCircuit(
264
- left: [BaseOrMergeRollupPublicInputs, Proof],
265
- right: [BaseOrMergeRollupPublicInputs, Proof],
266
- ): Promise<[BaseOrMergeRollupPublicInputs, Proof]> {
267
- const vk = this.getVerificationKey(left[0].rollupType);
268
- const mergeInputs = new MergeRollupInputs([
269
- this.getPreviousRollupDataFromPublicInputs(left[0], left[1], vk),
270
- this.getPreviousRollupDataFromPublicInputs(right[0], right[1], vk),
271
- ]);
272
-
273
- this.debug(`Running merge rollup circuit`);
274
- const output = await this.simulator.mergeRollupCircuit(mergeInputs);
275
- const proof = await this.prover.getMergeRollupProof(mergeInputs, output);
276
- return [output, proof];
277
- }
278
-
279
- protected getVerificationKey(type: RollupTypes) {
280
- switch (type) {
281
- case RollupTypes.Base:
282
- return this.vks.baseRollupCircuit;
283
- case RollupTypes.Merge:
284
- return this.vks.mergeRollupCircuit;
285
- default:
286
- throw new Error(`No verification key available for ${type}`);
287
- }
288
- }
289
-
290
- protected async rootRollupCircuit(
291
- left: [BaseOrMergeRollupPublicInputs, Proof],
292
- right: [BaseOrMergeRollupPublicInputs, Proof],
293
- newL1ToL2Messages: Tuple<Fr, typeof NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP>,
294
- ): Promise<[RootRollupPublicInputs, Proof]> {
295
- this.debug(`Running root rollup circuit`);
296
- const rootInput = await this.getRootRollupInput(...left, ...right, newL1ToL2Messages);
297
-
298
- // Update the local trees to include the new l1 to l2 messages
299
- await this.db.appendLeaves(
300
- MerkleTreeId.L1_TO_L2_MESSAGES_TREE,
301
- newL1ToL2Messages.map(m => m.toBuffer()),
302
- );
303
-
304
- // Simulate and get proof for the root circuit
305
- const rootOutput = await this.simulator.rootRollupCircuit(rootInput);
306
-
307
- const rootProof = await this.prover.getRootRollupProof(rootInput, rootOutput);
308
-
309
- // Update the root trees with the latest data and contract tree roots,
310
- // and validate them against the output of the root circuit simulation
311
- this.debug(`Updating and validating root trees`);
312
- const globalVariablesHash = computeGlobalsHash(left[0].constants.globalVariables);
313
- await this.db.updateLatestGlobalVariablesHash(globalVariablesHash);
314
- await this.db.updateBlocksTree(globalVariablesHash);
315
-
316
- await this.validateRootOutput(rootOutput);
317
-
318
- return [rootOutput, rootProof];
319
- }
320
-
321
- async updateBlocksTree(globalVariables: GlobalVariables) {
322
- // Calculate the block hash and add it to the historical block hashes tree
323
- const blockHash = await this.calculateBlockHash(globalVariables);
324
- await this.db.appendLeaves(MerkleTreeId.BLOCKS_TREE, [blockHash.toBuffer()]);
325
- }
326
-
327
- protected async calculateBlockHash(globals: GlobalVariables) {
328
- const [noteHashTreeRoot, nullifierTreeRoot, contractTreeRoot, publicDataTreeRoot, l1ToL2MessageTreeRoot] = (
329
- await Promise.all(
330
- [
331
- MerkleTreeId.NOTE_HASH_TREE,
332
- MerkleTreeId.NULLIFIER_TREE,
333
- MerkleTreeId.CONTRACT_TREE,
334
- MerkleTreeId.PUBLIC_DATA_TREE,
335
- MerkleTreeId.L1_TO_L2_MESSAGES_TREE,
336
- ].map(tree => this.getTreeSnapshot(tree)),
337
- )
338
- ).map(r => r.root);
339
-
340
- const blockHash = computeBlockHashWithGlobals(
341
- globals,
342
- noteHashTreeRoot,
343
- nullifierTreeRoot,
344
- contractTreeRoot,
345
- l1ToL2MessageTreeRoot,
346
- publicDataTreeRoot,
347
- );
348
- return blockHash;
349
- }
350
-
351
- // Validate that the new roots we calculated from manual insertions match the outputs of the simulation
352
- protected async validateTrees(rollupOutput: BaseOrMergeRollupPublicInputs | RootRollupPublicInputs) {
353
- await Promise.all([
354
- this.validateTree(rollupOutput, MerkleTreeId.CONTRACT_TREE, 'Contract'),
355
- this.validateTree(rollupOutput, MerkleTreeId.NOTE_HASH_TREE, 'NoteHash'),
356
- this.validateTree(rollupOutput, MerkleTreeId.NULLIFIER_TREE, 'Nullifier'),
357
- this.validatePublicDataTreeRoot(rollupOutput),
358
- ]);
359
- }
360
-
361
- // Validate that the roots of all local trees match the output of the root circuit simulation
362
- protected async validateRootOutput(rootOutput: RootRollupPublicInputs) {
363
- await Promise.all([
364
- this.validateTrees(rootOutput),
365
- this.validateTree(rootOutput, MerkleTreeId.BLOCKS_TREE, 'Blocks'),
366
- this.validateTree(rootOutput, MerkleTreeId.L1_TO_L2_MESSAGES_TREE, 'L1ToL2Messages'),
367
- ]);
368
- }
369
-
370
- /**
371
- * Validates that the root of the public data tree matches the output of the circuit simulation.
372
- * @param output - The output of the circuit simulation.
373
- * Note: Public data tree is sparse, so the "next available leaf index" doesn't make sense there.
374
- * For this reason we only validate root.
375
- */
376
- protected async validatePublicDataTreeRoot(output: BaseOrMergeRollupPublicInputs | RootRollupPublicInputs) {
377
- const localTree = await this.getTreeSnapshot(MerkleTreeId.PUBLIC_DATA_TREE);
378
- const simulatedTreeRoot = output[`endPublicDataTreeRoot`];
379
-
380
- if (!simulatedTreeRoot.toBuffer().equals(localTree.root.toBuffer())) {
381
- throw new Error(`PublicData tree root mismatch (local ${localTree.root}, simulated ${simulatedTreeRoot})`);
382
- }
383
- }
384
-
385
- // Helper for validating a non-roots tree against a circuit simulation output
386
- protected async validateTree<T extends BaseOrMergeRollupPublicInputs | RootRollupPublicInputs>(
387
- output: T,
388
- treeId: MerkleTreeId,
389
- name: AllowedTreeNames<T>,
390
- ) {
391
- if ('endL1ToL2MessageTreeSnapshot' in output && !(output instanceof RootRollupPublicInputs)) {
392
- throw new Error(`The name 'L1ToL2Message' can only be used when output is of type RootRollupPublicInputs`);
393
- }
394
-
395
- const localTree = await this.getTreeSnapshot(treeId);
396
- const simulatedTree = (output as OutputWithTreeSnapshot<T>)[`end${name}TreeSnapshot`];
397
- this.validateSimulatedTree(localTree, simulatedTree, name);
398
- }
399
-
400
- // Helper for comparing two trees snapshots
401
- protected validateSimulatedTree(
402
- localTree: AppendOnlyTreeSnapshot,
403
- simulatedTree: AppendOnlyTreeSnapshot,
404
- name: 'NoteHash' | 'Contract' | 'Nullifier' | 'L1ToL2Messages' | 'Blocks',
405
- label?: string,
406
- ) {
407
- if (!simulatedTree.root.toBuffer().equals(localTree.root.toBuffer())) {
408
- throw new Error(`${label ?? name} tree root mismatch (local ${localTree.root}, simulated ${simulatedTree.root})`);
409
- }
410
- if (simulatedTree.nextAvailableLeafIndex !== localTree.nextAvailableLeafIndex) {
411
- throw new Error(
412
- `${label ?? name} tree next available leaf index mismatch (local ${
413
- localTree.nextAvailableLeafIndex
414
- }, simulated ${simulatedTree.nextAvailableLeafIndex})`,
415
- );
416
- }
417
- }
418
-
419
- // Builds the inputs for the root rollup circuit, without making any changes to trees
420
- protected async getRootRollupInput(
421
- rollupOutputLeft: BaseOrMergeRollupPublicInputs,
422
- rollupProofLeft: Proof,
423
- rollupOutputRight: BaseOrMergeRollupPublicInputs,
424
- rollupProofRight: Proof,
425
- newL1ToL2Messages: Tuple<Fr, typeof NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP>,
426
- ) {
427
- const vk = this.getVerificationKey(rollupOutputLeft.rollupType);
428
- const previousRollupData: RootRollupInputs['previousRollupData'] = [
429
- this.getPreviousRollupDataFromPublicInputs(rollupOutputLeft, rollupProofLeft, vk),
430
- this.getPreviousRollupDataFromPublicInputs(rollupOutputRight, rollupProofRight, vk),
431
- ];
432
-
433
- const getRootTreeSiblingPath = async (treeId: MerkleTreeId) => {
434
- // TODO: Synchronize these operations into the tree db to avoid race conditions
435
- const { size } = await this.db.getTreeInfo(treeId);
436
- // TODO: Check for off-by-one errors
437
- const path = await this.db.getSiblingPath(treeId, size);
438
- return path.toFieldArray();
439
- };
440
-
441
- const newL1ToL2MessagesTreeRootSiblingPathArray = await this.getSubtreeSiblingPath(
442
- MerkleTreeId.L1_TO_L2_MESSAGES_TREE,
443
- L1_TO_L2_MSG_SUBTREE_HEIGHT,
444
- );
445
-
446
- const newL1ToL2MessagesTreeRootSiblingPath = makeTuple(
447
- L1_TO_L2_MSG_SUBTREE_SIBLING_PATH_LENGTH,
448
- i =>
449
- i < newL1ToL2MessagesTreeRootSiblingPathArray.length ? newL1ToL2MessagesTreeRootSiblingPathArray[i] : Fr.ZERO,
450
- 0,
451
- );
452
-
453
- // Get tree snapshots
454
- const startL1ToL2MessagesTreeSnapshot = await this.getTreeSnapshot(MerkleTreeId.L1_TO_L2_MESSAGES_TREE);
455
-
456
- // Get blocks tree
457
- const startBlocksTreeSnapshot = await this.getTreeSnapshot(MerkleTreeId.BLOCKS_TREE);
458
- const newBlocksTreeSiblingPathArray = await getRootTreeSiblingPath(MerkleTreeId.BLOCKS_TREE);
459
-
460
- const newBlocksTreeSiblingPath = makeTuple(
461
- BLOCKS_TREE_HEIGHT,
462
- i => (i < newBlocksTreeSiblingPathArray.length ? newBlocksTreeSiblingPathArray[i] : Fr.ZERO),
463
- 0,
464
- );
465
-
466
- return RootRollupInputs.from({
467
- previousRollupData,
468
- newL1ToL2Messages,
469
- newL1ToL2MessagesTreeRootSiblingPath,
470
- startL1ToL2MessagesTreeSnapshot,
471
- startBlocksTreeSnapshot,
472
- newBlocksTreeSiblingPath,
473
- });
474
- }
475
-
476
- protected getPreviousRollupDataFromPublicInputs(
477
- rollupOutput: BaseOrMergeRollupPublicInputs,
478
- rollupProof: Proof,
479
- vk: VerificationKey,
480
- ) {
481
- return new PreviousRollupData(
482
- rollupOutput,
483
- rollupProof,
484
- vk,
485
-
486
- // MembershipWitness for a VK tree to be implemented in the future
487
- FUTURE_NUM,
488
- new MembershipWitness(
489
- ROLLUP_VK_TREE_HEIGHT,
490
- BigInt(FUTURE_NUM),
491
- makeTuple(ROLLUP_VK_TREE_HEIGHT, () => FUTURE_FR),
492
- ),
493
- );
494
- }
495
-
496
- protected getKernelDataFor(tx: ProcessedTx) {
497
- return new PreviousKernelData(
498
- tx.data,
499
- tx.proof,
500
-
501
- // VK for the kernel circuit
502
- this.vks.privateKernelCircuit,
503
-
504
- // MembershipWitness for a VK tree to be implemented in the future
505
- FUTURE_NUM,
506
- assertLength(Array(VK_TREE_HEIGHT).fill(FUTURE_FR), VK_TREE_HEIGHT),
507
- );
508
- }
509
-
510
- // Scan a tree searching for a specific value and return a membership witness proof for it
511
- protected async getMembershipWitnessFor<N extends number>(
512
- value: Fr,
513
- treeId: MerkleTreeId,
514
- height: N,
515
- ): Promise<MembershipWitness<N>> {
516
- // If this is an empty tx, then just return zeroes
517
- if (value.isZero()) {
518
- return this.makeEmptyMembershipWitness(height);
519
- }
520
-
521
- const index = await this.db.findLeafIndex(treeId, value.toBuffer());
522
- if (index === undefined) {
523
- throw new Error(`Leaf with value ${value} not found in tree ${MerkleTreeId[treeId]}`);
524
- }
525
- const path = await this.db.getSiblingPath(treeId, index);
526
- return new MembershipWitness(height, index, assertLength(path.toFieldArray(), height));
527
- }
528
-
529
- protected getHistoricalTreesMembershipWitnessFor(tx: ProcessedTx) {
530
- const blockHeader = tx.data.constants.blockHeader;
531
- const { noteHashTreeRoot, nullifierTreeRoot, contractTreeRoot, l1ToL2MessagesTreeRoot, publicDataTreeRoot } =
532
- blockHeader;
533
- const blockHash = computeBlockHash(
534
- blockHeader.globalVariablesHash,
535
- noteHashTreeRoot,
536
- nullifierTreeRoot,
537
- contractTreeRoot,
538
- l1ToL2MessagesTreeRoot,
539
- publicDataTreeRoot,
540
- );
541
- return this.getMembershipWitnessFor(blockHash, MerkleTreeId.BLOCKS_TREE, BLOCKS_TREE_HEIGHT);
542
- }
543
-
544
- protected async getConstantRollupData(globalVariables: GlobalVariables): Promise<ConstantRollupData> {
545
- return ConstantRollupData.from({
546
- baseRollupVkHash: DELETE_FR,
547
- mergeRollupVkHash: DELETE_FR,
548
- privateKernelVkTreeRoot: FUTURE_FR,
549
- publicKernelVkTreeRoot: FUTURE_FR,
550
- startBlocksTreeSnapshot: await this.getTreeSnapshot(MerkleTreeId.BLOCKS_TREE),
551
- globalVariables,
552
- });
553
- }
554
-
555
- protected async getLowNullifierInfo(nullifier: Fr) {
556
- // Return empty nullifier info for an empty tx
557
- if (nullifier.value === 0n) {
558
- return {
559
- index: 0,
560
- leafPreimage: NullifierLeafPreimage.empty(),
561
- witness: this.makeEmptyMembershipWitness(NULLIFIER_TREE_HEIGHT),
562
- };
563
- }
564
-
565
- const tree = MerkleTreeId.NULLIFIER_TREE;
566
- const prevValueIndex = await this.db.getPreviousValueIndex(tree, frToBigInt(nullifier));
567
- if (!prevValueIndex) {
568
- throw new Error(`Nullifier tree should have one initial leaf`);
569
- }
570
- const prevValuePreimage = (await this.db.getLeafPreimage(tree, prevValueIndex.index))!;
571
-
572
- const prevValueSiblingPath = await this.db.getSiblingPath(tree, BigInt(prevValueIndex.index));
573
-
574
- return {
575
- index: prevValueIndex,
576
- leafPreimage: prevValuePreimage,
577
- witness: new MembershipWitness(
578
- NULLIFIER_TREE_HEIGHT,
579
- BigInt(prevValueIndex.index),
580
- assertLength(prevValueSiblingPath.toFieldArray(), NULLIFIER_TREE_HEIGHT),
581
- ),
582
- };
583
- }
584
-
585
- protected async getSubtreeSiblingPath(treeId: MerkleTreeId, subtreeHeight: number): Promise<Fr[]> {
586
- const nextAvailableLeafIndex = await this.db.getTreeInfo(treeId).then(t => t.size);
587
- const fullSiblingPath = await this.db.getSiblingPath(treeId, nextAvailableLeafIndex);
588
-
589
- // Drop the first subtreeHeight items since we only care about the path to the subtree root
590
- return fullSiblingPath.getSubtreeSiblingPath(subtreeHeight).toFieldArray();
591
- }
592
-
593
- protected async processPublicDataUpdateRequests(tx: ProcessedTx) {
594
- const newPublicDataUpdateRequestsSiblingPaths: Tuple<
595
- Tuple<Fr, typeof PUBLIC_DATA_TREE_HEIGHT>,
596
- typeof MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX
597
- > = makeTuple(MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, () => makeTuple(PUBLIC_DATA_TREE_HEIGHT, Fr.zero));
598
- for (const i in tx.data.end.publicDataUpdateRequests) {
599
- const index = tx.data.end.publicDataUpdateRequests[i].leafIndex.value;
600
- await this.db.updateLeaf(
601
- MerkleTreeId.PUBLIC_DATA_TREE,
602
- tx.data.end.publicDataUpdateRequests[i].newValue.toBuffer(),
603
- index,
604
- );
605
- const path = await this.db.getSiblingPath(MerkleTreeId.PUBLIC_DATA_TREE, index);
606
- const array = path.toFieldArray();
607
- newPublicDataUpdateRequestsSiblingPaths[i] = makeTuple(PUBLIC_DATA_TREE_HEIGHT, j =>
608
- j < array.length ? array[j] : Fr.ZERO,
609
- );
610
- }
611
- return newPublicDataUpdateRequestsSiblingPaths;
612
- }
613
-
614
- protected async getPublicDataReadsSiblingPaths(tx: ProcessedTx) {
615
- const newPublicDataReadsSiblingPaths: Tuple<
616
- Tuple<Fr, typeof PUBLIC_DATA_TREE_HEIGHT>,
617
- typeof MAX_PUBLIC_DATA_READS_PER_TX
618
- > = makeTuple(MAX_PUBLIC_DATA_READS_PER_TX, () => makeTuple(PUBLIC_DATA_TREE_HEIGHT, Fr.zero));
619
- for (const i in tx.data.end.publicDataReads) {
620
- const index = tx.data.end.publicDataReads[i].leafIndex.value;
621
- const path = await this.db.getSiblingPath(MerkleTreeId.PUBLIC_DATA_TREE, index);
622
- const array = path.toFieldArray();
623
- newPublicDataReadsSiblingPaths[i] = makeTuple(PUBLIC_DATA_TREE_HEIGHT, j =>
624
- j < array.length ? array[j] : Fr.ZERO,
625
- );
626
- }
627
- return newPublicDataReadsSiblingPaths;
628
- }
629
-
630
- // Builds the base rollup inputs, updating the contract, nullifier, and data trees in the process
631
- protected async buildBaseRollupInput(left: ProcessedTx, right: ProcessedTx, globalVariables: GlobalVariables) {
632
- // Get trees info before any changes hit
633
- const constants = await this.getConstantRollupData(globalVariables);
634
- const startNullifierTreeSnapshot = await this.getTreeSnapshot(MerkleTreeId.NULLIFIER_TREE);
635
- const startContractTreeSnapshot = await this.getTreeSnapshot(MerkleTreeId.CONTRACT_TREE);
636
- const startNoteHashTreeSnapshot = await this.getTreeSnapshot(MerkleTreeId.NOTE_HASH_TREE);
637
- const startPublicDataTreeSnapshot = await this.getTreeSnapshot(MerkleTreeId.PUBLIC_DATA_TREE);
638
- const startBlocksTreeSnapshot = await this.getTreeSnapshot(MerkleTreeId.BLOCKS_TREE);
639
-
640
- // Get the subtree sibling paths for the circuit
641
- const newCommitmentsSubtreeSiblingPathArray = await this.getSubtreeSiblingPath(
642
- MerkleTreeId.NOTE_HASH_TREE,
643
- NOTE_HASH_SUBTREE_HEIGHT,
644
- );
645
-
646
- const newCommitmentsSubtreeSiblingPath = makeTuple(NOTE_HASH_SUBTREE_SIBLING_PATH_LENGTH, i =>
647
- i < newCommitmentsSubtreeSiblingPathArray.length ? newCommitmentsSubtreeSiblingPathArray[i] : Fr.ZERO,
648
- );
649
-
650
- const newContractsSubtreeSiblingPathArray = await this.getSubtreeSiblingPath(
651
- MerkleTreeId.CONTRACT_TREE,
652
- CONTRACT_SUBTREE_HEIGHT,
653
- );
654
-
655
- const newContractsSubtreeSiblingPath = makeTuple(CONTRACT_SUBTREE_SIBLING_PATH_LENGTH, i =>
656
- i < newContractsSubtreeSiblingPathArray.length ? newContractsSubtreeSiblingPathArray[i] : Fr.ZERO,
657
- );
658
-
659
- // Update the contract and note hash trees with the new items being inserted to get the new roots
660
- // that will be used by the next iteration of the base rollup circuit, skipping the empty ones
661
- const newContracts = flatMap([left, right], tx => tx.data.end.newContracts.map(cd => computeContractLeaf(cd)));
662
- const newCommitments = flatMap([left, right], tx => tx.data.end.newCommitments.map(x => x.toBuffer()));
663
- await this.db.appendLeaves(
664
- MerkleTreeId.CONTRACT_TREE,
665
- newContracts.map(x => x.toBuffer()),
666
- );
667
-
668
- await this.db.appendLeaves(MerkleTreeId.NOTE_HASH_TREE, newCommitments);
669
-
670
- // Update the public data tree and get membership witnesses.
671
- // All public data reads are checked against the unmodified data root when the corresponding tx started,
672
- // so it's the unmodified tree for tx1, and the one after applying tx1 update request for tx2.
673
- // Update requests are checked against the tree as it is iteratively updated.
674
- // See https://github.com/AztecProtocol/aztec3-packages/issues/270#issuecomment-1522258200
675
- const leftPublicDataReadSiblingPaths = await this.getPublicDataReadsSiblingPaths(left);
676
- const leftPublicDataUpdateRequestsSiblingPaths = await this.processPublicDataUpdateRequests(left);
677
- const rightPublicDataReadSiblingPaths = await this.getPublicDataReadsSiblingPaths(right);
678
- const rightPublicDataUpdateRequestsSiblingPaths = await this.processPublicDataUpdateRequests(right);
679
-
680
- const newPublicDataReadsSiblingPaths = makeTuple(MAX_PUBLIC_DATA_READS_PER_BASE_ROLLUP, i =>
681
- i < MAX_PUBLIC_DATA_READS_PER_TX
682
- ? leftPublicDataReadSiblingPaths[i]
683
- : rightPublicDataReadSiblingPaths[i - MAX_PUBLIC_DATA_READS_PER_TX],
684
- );
685
-
686
- const newPublicDataUpdateRequestsSiblingPaths = makeTuple(MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_BASE_ROLLUP, i =>
687
- i < MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX
688
- ? leftPublicDataUpdateRequestsSiblingPaths[i]
689
- : rightPublicDataUpdateRequestsSiblingPaths[i - MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX],
690
- );
691
-
692
- // Update the nullifier tree, capturing the low nullifier info for each individual operation
693
- const newNullifiers = [...left.data.end.newNullifiers, ...right.data.end.newNullifiers];
694
-
695
- const {
696
- lowLeavesWitnessData: nullifierWitnessLeaves,
697
- newSubtreeSiblingPath: newNullifiersSubtreeSiblingPath,
698
- sortedNewLeaves: sortedNewNullifiers,
699
- sortedNewLeavesIndexes: sortednewNullifiersIndexes,
700
- } = await this.db.batchInsert(
701
- MerkleTreeId.NULLIFIER_TREE,
702
- newNullifiers.map(fr => fr.toBuffer()),
703
- NULLIFIER_SUBTREE_HEIGHT,
704
- );
705
- if (nullifierWitnessLeaves === undefined) {
706
- throw new Error(`Could not craft nullifier batch insertion proofs`);
707
- }
708
-
709
- // Extract witness objects from returned data
710
- const lowNullifierMembershipWitnesses: MembershipWitness<typeof NULLIFIER_TREE_HEIGHT>[] =
711
- nullifierWitnessLeaves.map(l =>
712
- MembershipWitness.fromBufferArray(l.index, assertLength(l.siblingPath.toBufferArray(), NULLIFIER_TREE_HEIGHT)),
713
- );
714
-
715
- const newNullifiersSubtreeSiblingPathArray = newNullifiersSubtreeSiblingPath.toFieldArray();
716
-
717
- return BaseRollupInputs.from({
718
- constants,
719
- startNullifierTreeSnapshot,
720
- startContractTreeSnapshot,
721
- startNoteHashTreeSnapshot,
722
- startPublicDataTreeRoot: startPublicDataTreeSnapshot.root,
723
- startBlocksTreeSnapshot,
724
- sortedNewNullifiers: makeTuple(MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP, i => Fr.fromBuffer(sortedNewNullifiers[i])),
725
- sortednewNullifiersIndexes: makeTuple(MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP, i => sortednewNullifiersIndexes[i]),
726
- newCommitmentsSubtreeSiblingPath,
727
- newContractsSubtreeSiblingPath,
728
- newNullifiersSubtreeSiblingPath: makeTuple(NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH, i =>
729
- i < newNullifiersSubtreeSiblingPathArray.length ? newNullifiersSubtreeSiblingPathArray[i] : Fr.ZERO,
730
- ),
731
- newPublicDataUpdateRequestsSiblingPaths,
732
- newPublicDataReadsSiblingPaths,
733
- lowNullifierLeafPreimages: makeTuple(MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP, i =>
734
- i < nullifierWitnessLeaves.length
735
- ? (nullifierWitnessLeaves[i].leafPreimage as NullifierLeafPreimage)
736
- : NullifierLeafPreimage.empty(),
737
- ),
738
- lowNullifierMembershipWitness: makeTuple(MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP, i =>
739
- i < lowNullifierMembershipWitnesses.length
740
- ? lowNullifierMembershipWitnesses[i]
741
- : this.makeEmptyMembershipWitness(NULLIFIER_TREE_HEIGHT),
742
- ),
743
- kernelData: [this.getKernelDataFor(left), this.getKernelDataFor(right)],
744
- blocksTreeRootMembershipWitnesses: [
745
- await this.getHistoricalTreesMembershipWitnessFor(left),
746
- await this.getHistoricalTreesMembershipWitnessFor(right),
747
- ],
748
- });
749
- }
750
-
751
- protected makeEmptyMembershipWitness<N extends number>(height: N) {
752
- return new MembershipWitness(
753
- height,
754
- 0n,
755
- makeTuple(height, () => Fr.ZERO),
756
- );
757
- }
758
- }