@aztec/sequencer-client 0.1.0-alpha43 → 0.1.0-alpha45

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.
@@ -1,4 +1,11 @@
1
- import { PublicExecution, PublicExecutionResult, PublicExecutor, isPublicExecutionResult } from '@aztec/acir-simulator';
1
+ import {
2
+ PublicExecution,
3
+ PublicExecutionResult,
4
+ PublicExecutor,
5
+ collectPublicDataReads,
6
+ collectPublicDataUpdateRequests,
7
+ isPublicExecutionResult,
8
+ } from '@aztec/acir-simulator';
2
9
  import {
3
10
  AztecAddress,
4
11
  CircuitsWasm,
@@ -13,20 +20,24 @@ import {
13
20
  MAX_NEW_NULLIFIERS_PER_CALL,
14
21
  MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL,
15
22
  MAX_PUBLIC_DATA_READS_PER_CALL,
23
+ MAX_PUBLIC_DATA_READS_PER_TX,
16
24
  MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL,
25
+ MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX,
17
26
  MembershipWitness,
18
27
  PreviousKernelData,
19
28
  Proof,
20
29
  PublicCallData,
21
30
  PublicCallStackItem,
22
31
  PublicCircuitPublicInputs,
32
+ PublicDataRead,
33
+ PublicDataUpdateRequest,
23
34
  PublicKernelInputs,
24
35
  PublicKernelPublicInputs,
25
36
  RETURN_VALUES_LENGTH,
26
37
  VK_TREE_HEIGHT,
27
38
  } from '@aztec/circuits.js';
28
39
  import { computeCallStackItemHash, computeVarArgsHash } from '@aztec/circuits.js/abis';
29
- import { isArrayEmpty, padArrayEnd, padArrayStart } from '@aztec/foundation/collection';
40
+ import { arrayNonEmptyLength, isArrayEmpty, padArrayEnd, padArrayStart } from '@aztec/foundation/collection';
30
41
  import { createDebugLogger } from '@aztec/foundation/log';
31
42
  import { Tuple, mapTuple, to2Fields } from '@aztec/foundation/serialize';
32
43
  import { ContractDataSource, FunctionL2Logs, L1ToL2MessageSource, MerkleTreeId, Tx } from '@aztec/types';
@@ -138,24 +149,42 @@ export class PublicProcessor {
138
149
  this.log(`Executing enqueued public calls for tx ${await tx.getTxHash()}`);
139
150
  if (!tx.enqueuedPublicFunctionCalls) throw new Error(`Missing preimages for enqueued public calls`);
140
151
 
141
- const executionStack: (PublicExecution | PublicExecutionResult)[] = [...tx.enqueuedPublicFunctionCalls];
142
-
143
152
  let kernelOutput = tx.data;
144
153
  let kernelProof = tx.proof;
145
154
  const newUnencryptedFunctionLogs: FunctionL2Logs[] = [];
146
155
 
147
- while (executionStack.length) {
148
- const current = executionStack.pop()!;
149
- const isExecutionRequest = !isPublicExecutionResult(current);
150
- const result = isExecutionRequest ? await this.publicExecutor.execute(current, this.globalVariables) : current;
151
- newUnencryptedFunctionLogs.push(result.unencryptedLogs);
152
- const functionSelector = result.execution.functionData.functionSelectorBuffer.toString('hex');
153
- this.log(`Running public kernel circuit for ${functionSelector}@${result.execution.contractAddress.toString()}`);
154
- executionStack.push(...result.nestedExecutions);
155
- const preimages = await this.getPublicCallStackPreimages(result);
156
- const callData = await this.getPublicCallData(result, preimages, isExecutionRequest);
157
-
158
- [kernelOutput, kernelProof] = await this.runKernelCircuit(callData, kernelOutput, kernelProof);
156
+ // TODO(#1684): Should multiple separately enqueued public calls be treated as
157
+ // separate public callstacks to be proven by separate public kernel sequences
158
+ // and submitted separately to the base rollup?
159
+
160
+ // TODO(dbanks12): why must these be reversed?
161
+ const enqueuedCallsReversed = tx.enqueuedPublicFunctionCalls.slice().reverse();
162
+ for (const enqueuedCall of enqueuedCallsReversed) {
163
+ const executionStack: (PublicExecution | PublicExecutionResult)[] = [enqueuedCall];
164
+
165
+ // Keep track of which result is for the top/enqueued call
166
+ let enqueuedExecutionResult: PublicExecutionResult | undefined;
167
+
168
+ while (executionStack.length) {
169
+ const current = executionStack.pop()!;
170
+ const isExecutionRequest = !isPublicExecutionResult(current);
171
+ const result = isExecutionRequest ? await this.publicExecutor.execute(current, this.globalVariables) : current;
172
+ newUnencryptedFunctionLogs.push(result.unencryptedLogs);
173
+ const functionSelector = result.execution.functionData.functionSelectorBuffer.toString('hex');
174
+ this.log(
175
+ `Running public kernel circuit for ${functionSelector}@${result.execution.contractAddress.toString()}`,
176
+ );
177
+ executionStack.push(...result.nestedExecutions);
178
+ const preimages = await this.getPublicCallStackPreimages(result);
179
+ const callData = await this.getPublicCallData(result, preimages, isExecutionRequest);
180
+
181
+ [kernelOutput, kernelProof] = await this.runKernelCircuit(callData, kernelOutput, kernelProof);
182
+
183
+ if (!enqueuedExecutionResult) enqueuedExecutionResult = result;
184
+ }
185
+ // HACK(#1622): Manually patches the ordering of public state actions
186
+ // TODO(#757): Enforce proper ordering of public state actions
187
+ await this.patchPublicStorageActionOrdering(kernelOutput, enqueuedExecutionResult!);
159
188
  }
160
189
 
161
190
  return [kernelOutput, kernelProof, newUnencryptedFunctionLogs];
@@ -286,4 +315,96 @@ export class PublicProcessor {
286
315
  const proof = await this.publicProver.getPublicCircuitProof(callStackItem.publicInputs);
287
316
  return new PublicCallData(callStackItem, preimages, proof, portalContractAddress, bytecodeHash);
288
317
  }
318
+
319
+ // HACK(#1622): this is a hack to fix ordering of public state in the call stack. Since the private kernel
320
+ // cannot keep track of side effects that happen after or before a nested call, we override the public
321
+ // state actions it emits with whatever we got from the simulator. As a sanity check, we at least verify
322
+ // that the elements are the same, so we are only tweaking their ordering.
323
+ // See yarn-project/end-to-end/src/e2e_ordering.test.ts
324
+ // See https://github.com/AztecProtocol/aztec-packages/issues/1616
325
+ // TODO(#757): Enforce proper ordering of public state actions
326
+ /**
327
+ * Patch the ordering of storage actions output from the public kernel.
328
+ * @param publicInputs - to be patched here: public inputs to the kernel iteration up to this point
329
+ * @param execResult - result of the top/first execution for this enqueued public call
330
+ */
331
+ private async patchPublicStorageActionOrdering(
332
+ publicInputs: KernelCircuitPublicInputs,
333
+ execResult: PublicExecutionResult,
334
+ ) {
335
+ // Convert ContractStorage* objects to PublicData* objects and sort them in execution order
336
+ const wasm = await CircuitsWasm.get();
337
+ const simPublicDataReads = collectPublicDataReads(wasm, execResult);
338
+ const simPublicDataUpdateRequests = collectPublicDataUpdateRequests(wasm, execResult);
339
+
340
+ const { publicDataReads, publicDataUpdateRequests } = publicInputs.end; // from kernel
341
+
342
+ // Validate all items in enqueued public calls are in the kernel emitted stack
343
+ const readsAreEqual = simPublicDataReads.reduce(
344
+ (accum, read) =>
345
+ accum && !!publicDataReads.find(item => item.leafIndex.equals(read.leafIndex) && item.value.equals(read.value)),
346
+ true,
347
+ );
348
+ const updatesAreEqual = simPublicDataUpdateRequests.reduce(
349
+ (accum, update) =>
350
+ accum &&
351
+ !!publicDataUpdateRequests.find(
352
+ item =>
353
+ item.leafIndex.equals(update.leafIndex) &&
354
+ item.oldValue.equals(update.oldValue) &&
355
+ item.newValue.equals(update.newValue),
356
+ ),
357
+ true,
358
+ );
359
+
360
+ if (!readsAreEqual) {
361
+ throw new Error(
362
+ `Public data reads from simulator do not match those from public kernel.\nFrom simulator: ${simPublicDataReads
363
+ .map(p => p.toFriendlyJSON())
364
+ .join(', ')}\nFrom public kernel: ${publicDataReads.map(i => i.toFriendlyJSON()).join(', ')}`,
365
+ );
366
+ }
367
+ if (!updatesAreEqual) {
368
+ throw new Error(
369
+ `Public data update requests from simulator do not match those from public kernel.\nFrom simulator: ${simPublicDataUpdateRequests
370
+ .map(p => p.toFriendlyJSON())
371
+ .join(', ')}\nFrom public kernel: ${publicDataUpdateRequests.map(i => i.toFriendlyJSON()).join(', ')}`,
372
+ );
373
+ }
374
+
375
+ // Assume that kernel public inputs has the right number of items.
376
+ // We only want to reorder the items from the public inputs of the
377
+ // most recently processed top/enqueued call.
378
+ const numTotalReadsInKernel = arrayNonEmptyLength(
379
+ publicInputs.end.publicDataReads,
380
+ f => f.leafIndex.equals(Fr.ZERO) && f.value.equals(Fr.ZERO),
381
+ );
382
+ const numTotalUpdatesInKernel = arrayNonEmptyLength(
383
+ publicInputs.end.publicDataUpdateRequests,
384
+ f => f.leafIndex.equals(Fr.ZERO) && f.oldValue.equals(Fr.ZERO) && f.newValue.equals(Fr.ZERO),
385
+ );
386
+ const numReadsBeforeThisEnqueuedCall = numTotalReadsInKernel - simPublicDataReads.length;
387
+ const numUpdatesBeforeThisEnqueuedCall = numTotalUpdatesInKernel - simPublicDataUpdateRequests.length;
388
+
389
+ // Override kernel output
390
+ publicInputs.end.publicDataReads = padArrayEnd(
391
+ [
392
+ // do not mess with items from previous top/enqueued calls in kernel output
393
+ ...publicDataReads.slice(0, numReadsBeforeThisEnqueuedCall),
394
+ ...simPublicDataReads,
395
+ ],
396
+ PublicDataRead.empty(),
397
+ MAX_PUBLIC_DATA_READS_PER_TX,
398
+ );
399
+ // Override kernel output
400
+ publicInputs.end.publicDataUpdateRequests = padArrayEnd(
401
+ [
402
+ // do not mess with items from previous top/enqueued calls in kernel output
403
+ ...publicDataUpdateRequests.slice(0, numUpdatesBeforeThisEnqueuedCall),
404
+ ...simPublicDataUpdateRequests,
405
+ ],
406
+ PublicDataUpdateRequest.empty(),
407
+ MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX,
408
+ );
409
+ }
289
410
  }
@@ -65,7 +65,7 @@ describe('sequencer', () => {
65
65
  });
66
66
 
67
67
  l2BlockSource = mock<L2BlockSource>({
68
- getBlockHeight: () => Promise.resolve(lastBlockNumber),
68
+ getBlockNumber: () => Promise.resolve(lastBlockNumber),
69
69
  });
70
70
 
71
71
  l1ToL2MessageSource = mock<L1ToL2MessageSource>({
@@ -140,7 +140,7 @@ export class Sequencer {
140
140
  this.log(`Processing ${validTxs.length} txs...`);
141
141
  this.state = SequencerState.CREATING_BLOCK;
142
142
 
143
- const blockNumber = (await this.l2BlockSource.getBlockHeight()) + 1;
143
+ const blockNumber = (await this.l2BlockSource.getBlockNumber()) + 1;
144
144
  const newGlobalVariables = await this.globalsBuilder.buildGlobalVariables(new Fr(blockNumber));
145
145
  const prevGlobalVariables = (await this.l2BlockSource.getL2Block(-1))?.globalVariables ?? GlobalVariables.empty();
146
146
 
@@ -11,7 +11,7 @@ export async function getHistoricBlockData(
11
11
  ) {
12
12
  const wasm = await CircuitsWasm.get();
13
13
  const prevGlobalsHash = computeGlobalsHash(wasm, prevBlockGlobalVariables);
14
- const roots = db.getTreeRoots();
14
+ const roots = await db.getTreeRoots();
15
15
 
16
16
  return new HistoricBlockData(
17
17
  Fr.fromBuffer(roots.privateDataTreeRoot),