@aztec/sequencer-client 0.16.4 → 0.16.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dest/block_builder/solo_block_builder.d.ts +3 -3
- package/dest/block_builder/solo_block_builder.d.ts.map +1 -1
- package/dest/block_builder/solo_block_builder.js +26 -26
- package/dest/block_builder/types.d.ts +11 -2
- package/dest/block_builder/types.d.ts.map +1 -1
- package/dest/sequencer/utils.js +2 -2
- package/package.json +11 -11
- package/src/block_builder/index.ts +0 -24
- package/src/block_builder/solo_block_builder.ts +0 -758
- package/src/block_builder/types.ts +0 -16
- package/src/client/index.ts +0 -1
- package/src/client/sequencer-client.ts +0 -87
- package/src/config.ts +0 -76
- package/src/global_variable_builder/config.ts +0 -20
- package/src/global_variable_builder/global_builder.ts +0 -87
- package/src/global_variable_builder/index.ts +0 -16
- package/src/global_variable_builder/viem-reader.ts +0 -61
- package/src/index.ts +0 -15
- package/src/mocks/verification_keys.ts +0 -36
- package/src/prover/empty.ts +0 -74
- package/src/prover/index.ts +0 -53
- package/src/publisher/config.ts +0 -41
- package/src/publisher/index.ts +0 -14
- package/src/publisher/l1-publisher.ts +0 -291
- package/src/publisher/viem-tx-sender.ts +0 -195
- package/src/receiver.ts +0 -13
- package/src/sequencer/config.ts +0 -1
- package/src/sequencer/index.ts +0 -3
- package/src/sequencer/processed_tx.ts +0 -107
- package/src/sequencer/public_processor.ts +0 -433
- package/src/sequencer/sequencer.ts +0 -434
- package/src/sequencer/utils.ts +0 -25
- package/src/simulator/index.ts +0 -51
- package/src/simulator/public_executor.ts +0 -153
- package/src/simulator/public_kernel.ts +0 -54
- package/src/simulator/rollup.ts +0 -76
- 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
|
-
}
|