@aztec/txe 0.72.1 → 0.74.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,10 @@
1
1
  import {
2
2
  AuthWitness,
3
+ Body,
4
+ L2Block,
3
5
  MerkleTreeId,
6
+ type MerkleTreeReadOperations,
7
+ type MerkleTreeWriteOperations,
4
8
  Note,
5
9
  type NoteStatus,
6
10
  NullifierMembershipWitness,
@@ -13,6 +17,7 @@ import {
13
17
  } from '@aztec/circuit-types';
14
18
  import { type CircuitWitnessGenerationStats } from '@aztec/circuit-types/stats';
15
19
  import {
20
+ AppendOnlyTreeSnapshot,
16
21
  BlockHeader,
17
22
  CallContext,
18
23
  type ContractInstance,
@@ -24,8 +29,11 @@ import {
24
29
  IndexedTaggingSecret,
25
30
  type KeyValidationRequest,
26
31
  type L1_TO_L2_MSG_TREE_HEIGHT,
32
+ MAX_NOTE_HASHES_PER_TX,
33
+ MAX_NULLIFIERS_PER_TX,
27
34
  NULLIFIER_SUBTREE_HEIGHT,
28
35
  type NULLIFIER_TREE_HEIGHT,
36
+ NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP,
29
37
  type NullifierLeafPreimage,
30
38
  PRIVATE_CONTEXT_INPUTS_LENGTH,
31
39
  type PUBLIC_DATA_TREE_HEIGHT,
@@ -34,7 +42,7 @@ import {
34
42
  type PrivateLog,
35
43
  PublicDataTreeLeaf,
36
44
  type PublicDataTreeLeafPreimage,
37
- type PublicDataWrite,
45
+ PublicDataWrite,
38
46
  type PublicLog,
39
47
  computeContractClassId,
40
48
  computeTaggingSecretPoint,
@@ -49,6 +57,12 @@ import {
49
57
  siloNoteHash,
50
58
  siloNullifier,
51
59
  } from '@aztec/circuits.js/hash';
60
+ import {
61
+ makeAppendOnlyTreeSnapshot,
62
+ makeContentCommitment,
63
+ makeGlobalVariables,
64
+ makeHeader,
65
+ } from '@aztec/circuits.js/testing';
52
66
  import {
53
67
  type ContractArtifact,
54
68
  type FunctionAbi,
@@ -57,6 +71,7 @@ import {
57
71
  countArgumentsSize,
58
72
  } from '@aztec/foundation/abi';
59
73
  import { AztecAddress } from '@aztec/foundation/aztec-address';
74
+ import { padArrayEnd } from '@aztec/foundation/collection';
60
75
  import { poseidon2Hash } from '@aztec/foundation/crypto';
61
76
  import { Fr } from '@aztec/foundation/fields';
62
77
  import { type LogFn, type Logger, applyStringFormatting, createDebugOnlyLogger } from '@aztec/foundation/log';
@@ -85,7 +100,7 @@ import {
85
100
  createSimulationError,
86
101
  resolveAssertionMessageFromError,
87
102
  } from '@aztec/simulator/server';
88
- import { MerkleTreeSnapshotOperationsFacade, type MerkleTrees } from '@aztec/world-state';
103
+ import { type NativeWorldStateService } from '@aztec/world-state';
89
104
 
90
105
  import { TXENode } from '../node/txe_node.js';
91
106
  import { type TXEDatabase } from '../util/txe_database.js';
@@ -93,7 +108,7 @@ import { TXEPublicContractDataSource } from '../util/txe_public_contract_data_so
93
108
  import { TXEWorldStateDB } from '../util/txe_world_state_db.js';
94
109
 
95
110
  export class TXE implements TypedOracle {
96
- private blockNumber = 0;
111
+ private blockNumber = 1;
97
112
  private sideEffectCounter = 0;
98
113
  private msgSender: AztecAddress;
99
114
  private functionSelector = FunctionSelector.fromField(new Fr(0));
@@ -104,6 +119,7 @@ export class TXE implements TypedOracle {
104
119
  private contractDataOracle: ContractDataOracle;
105
120
  private simulatorOracle: SimulatorOracle;
106
121
 
122
+ private publicDataWrites: PublicDataWrite[] = [];
107
123
  private uniqueNoteHashesFromPublic: Fr[] = [];
108
124
  private siloedNullifiersFromPublic: Fr[] = [];
109
125
  private privateLogs: PrivateLog[] = [];
@@ -124,16 +140,17 @@ export class TXE implements TypedOracle {
124
140
 
125
141
  private constructor(
126
142
  private logger: Logger,
127
- private trees: MerkleTrees,
128
143
  private executionCache: HashedValuesCache,
129
144
  private keyStore: KeyStore,
130
145
  private txeDatabase: TXEDatabase,
131
146
  private contractAddress: AztecAddress,
147
+ private nativeWorldStateService: NativeWorldStateService,
148
+ private baseFork: MerkleTreeWriteOperations,
132
149
  ) {
133
150
  this.noteCache = new ExecutionNoteCache(this.getTxRequestHash());
134
151
  this.contractDataOracle = new ContractDataOracle(txeDatabase);
135
152
 
136
- this.node = new TXENode(this.blockNumber, this.VERSION, this.CHAIN_ID, this.trees);
153
+ this.node = new TXENode(this.blockNumber, this.VERSION, this.CHAIN_ID, nativeWorldStateService, baseFork);
137
154
 
138
155
  // Default msg_sender (for entrypoints) is now Fr.max_value rather than 0 addr (see #7190 & #7404)
139
156
  this.msgSender = AztecAddress.fromField(Fr.MAX_FIELD_VALUE);
@@ -150,22 +167,31 @@ export class TXE implements TypedOracle {
150
167
 
151
168
  static async create(
152
169
  logger: Logger,
153
- trees: MerkleTrees,
154
170
  executionCache: HashedValuesCache,
155
171
  keyStore: KeyStore,
156
172
  txeDatabase: TXEDatabase,
173
+ nativeWorldStateService: NativeWorldStateService,
174
+ baseFork: MerkleTreeWriteOperations,
157
175
  ) {
158
- return new TXE(logger, trees, executionCache, keyStore, txeDatabase, await AztecAddress.random());
176
+ return new TXE(
177
+ logger,
178
+ executionCache,
179
+ keyStore,
180
+ txeDatabase,
181
+ await AztecAddress.random(),
182
+ nativeWorldStateService,
183
+ baseFork,
184
+ );
159
185
  }
160
186
 
161
187
  // Utils
162
188
 
163
- async #getTreesAt(blockNumber: number) {
164
- const db =
165
- blockNumber === (await this.getBlockNumber())
166
- ? await this.trees.getLatest()
167
- : new MerkleTreeSnapshotOperationsFacade(this.trees, blockNumber);
168
- return db;
189
+ getNativeWorldStateService() {
190
+ return this.nativeWorldStateService;
191
+ }
192
+
193
+ getBaseFork() {
194
+ return this.baseFork;
169
195
  }
170
196
 
171
197
  getChainId(): Promise<Fr> {
@@ -209,10 +235,6 @@ export class TXE implements TypedOracle {
209
235
  this.node.setBlockNumber(blockNumber);
210
236
  }
211
237
 
212
- getTrees() {
213
- return this.trees;
214
- }
215
-
216
238
  getContractDataOracle() {
217
239
  return this.contractDataOracle;
218
240
  }
@@ -230,8 +252,8 @@ export class TXE implements TypedOracle {
230
252
  }
231
253
 
232
254
  async addContractArtifact(artifact: ContractArtifact) {
233
- const contractClass = getContractClassFromArtifact(artifact);
234
- await this.txeDatabase.addContractArtifact(computeContractClassId(contractClass), artifact);
255
+ const contractClass = await getContractClassFromArtifact(artifact);
256
+ await this.txeDatabase.addContractArtifact(await computeContractClassId(contractClass), artifact);
235
257
  }
236
258
 
237
259
  async getPrivateContextInputs(
@@ -239,10 +261,21 @@ export class TXE implements TypedOracle {
239
261
  sideEffectsCounter = this.sideEffectCounter,
240
262
  isStaticCall = false,
241
263
  ) {
242
- const db = await this.#getTreesAt(blockNumber);
243
- const previousBlockState = await this.#getTreesAt(blockNumber - 1);
264
+ if (blockNumber > this.blockNumber) {
265
+ throw new Error(
266
+ `Tried to request private context inputs for ${blockNumber}, which is greater than our current block number of ${this.blockNumber}`,
267
+ );
268
+ } else if (blockNumber === this.blockNumber) {
269
+ this.logger.debug(
270
+ `Tried to request private context inputs for ${blockNumber}, equal to current block of ${this.blockNumber}. Clamping to current block - 1.`,
271
+ );
272
+ blockNumber = this.blockNumber - 1;
273
+ }
274
+
275
+ const snap = this.nativeWorldStateService.getSnapshot(blockNumber);
276
+ const previousBlockState = this.nativeWorldStateService.getSnapshot(blockNumber - 1);
244
277
 
245
- const stateReference = await db.getStateReference();
278
+ const stateReference = await snap.getStateReference();
246
279
  const inputs = PrivateContextInputs.empty();
247
280
  inputs.txContext.chainId = new Fr(await this.node.getChainId());
248
281
  inputs.txContext.version = new Fr(await this.node.getVersion());
@@ -270,26 +303,17 @@ export class TXE implements TypedOracle {
270
303
  }
271
304
 
272
305
  async addPublicDataWrites(writes: PublicDataWrite[]) {
273
- const db = await this.trees.getLatest();
274
- await db.batchInsert(
306
+ this.publicDataWrites.push(...writes);
307
+
308
+ await this.baseFork.sequentialInsert(
275
309
  MerkleTreeId.PUBLIC_DATA_TREE,
276
310
  writes.map(w => new PublicDataTreeLeaf(w.leafSlot, w.value).toBuffer()),
277
- 0,
278
- );
279
- }
280
-
281
- async addSiloedNullifiers(siloedNullifiers: Fr[]) {
282
- const db = await this.trees.getLatest();
283
- await db.batchInsert(
284
- MerkleTreeId.NULLIFIER_TREE,
285
- siloedNullifiers.map(n => n.toBuffer()),
286
- NULLIFIER_SUBTREE_HEIGHT,
287
311
  );
288
312
  }
289
313
 
290
314
  async checkNullifiersNotInTree(contractAddress: AztecAddress, nullifiers: Fr[]) {
291
- const siloedNullifiers = nullifiers.map(nullifier => siloNullifier(contractAddress, nullifier));
292
- const db = await this.trees.getLatest();
315
+ const siloedNullifiers = await Promise.all(nullifiers.map(nullifier => siloNullifier(contractAddress, nullifier)));
316
+ const db = this.baseFork;
293
317
  const nullifierIndexesInTree = await db.findLeafIndices(
294
318
  MerkleTreeId.NULLIFIER_TREE,
295
319
  siloedNullifiers.map(n => n.toBuffer()),
@@ -299,37 +323,18 @@ export class TXE implements TypedOracle {
299
323
  }
300
324
  }
301
325
 
302
- async addSiloedNullifiersFromPublic(siloedNullifiers: Fr[]) {
326
+ addSiloedNullifiersFromPublic(siloedNullifiers: Fr[]) {
303
327
  this.siloedNullifiersFromPublic.push(...siloedNullifiers);
304
-
305
- await this.addSiloedNullifiers(siloedNullifiers);
306
- }
307
-
308
- async addNullifiers(contractAddress: AztecAddress, nullifiers: Fr[]) {
309
- const siloedNullifiers = nullifiers.map(nullifier => siloNullifier(contractAddress, nullifier));
310
- await this.addSiloedNullifiers(siloedNullifiers);
311
- }
312
-
313
- async addUniqueNoteHashes(siloedNoteHashes: Fr[]) {
314
- const db = await this.trees.getLatest();
315
- await db.appendLeaves(MerkleTreeId.NOTE_HASH_TREE, siloedNoteHashes);
316
328
  }
317
329
 
318
- async addUniqueNoteHashesFromPublic(siloedNoteHashes: Fr[]) {
330
+ addUniqueNoteHashesFromPublic(siloedNoteHashes: Fr[]) {
319
331
  this.uniqueNoteHashesFromPublic.push(...siloedNoteHashes);
320
- await this.addUniqueNoteHashes(siloedNoteHashes);
321
332
  }
322
333
 
323
- async addNoteHashes(contractAddress: AztecAddress, noteHashes: Fr[]) {
324
- const siloedNoteHashes = noteHashes.map(noteHash => siloNoteHash(contractAddress, noteHash));
325
-
326
- await this.addUniqueNoteHashes(siloedNoteHashes);
327
- }
328
-
329
- addPrivateLogs(contractAddress: AztecAddress, privateLogs: PrivateLog[]) {
330
- privateLogs.forEach(privateLog => {
331
- privateLog.fields[0] = poseidon2Hash([contractAddress, privateLog.fields[0]]);
332
- });
334
+ async addPrivateLogs(contractAddress: AztecAddress, privateLogs: PrivateLog[]) {
335
+ for (const privateLog of privateLogs) {
336
+ privateLog.fields[0] = await poseidon2Hash([contractAddress, privateLog.fields[0]]);
337
+ }
333
338
 
334
339
  this.privateLogs.push(...privateLogs);
335
340
  }
@@ -371,7 +376,7 @@ export class TXE implements TypedOracle {
371
376
  }
372
377
 
373
378
  storeInExecutionCache(values: Fr[]) {
374
- return Promise.resolve(this.executionCache.store(values));
379
+ return this.executionCache.store(values);
375
380
  }
376
381
 
377
382
  loadFromExecutionCache(returnsHash: Fr) {
@@ -391,34 +396,36 @@ export class TXE implements TypedOracle {
391
396
  }
392
397
 
393
398
  async getMembershipWitness(blockNumber: number, treeId: MerkleTreeId, leafValue: Fr): Promise<Fr[] | undefined> {
394
- const db = await this.#getTreesAt(blockNumber);
395
- const index = (await db.findLeafIndices(treeId, [leafValue.toBuffer()]))[0];
399
+ const snap = this.nativeWorldStateService.getSnapshot(blockNumber);
400
+ const index = (await snap.findLeafIndices(treeId, [leafValue.toBuffer()]))[0];
396
401
  if (index === undefined) {
397
402
  throw new Error(`Leaf value: ${leafValue} not found in ${MerkleTreeId[treeId]} at block ${blockNumber}`);
398
403
  }
399
- const siblingPath = await db.getSiblingPath(treeId, index);
404
+ const siblingPath = await snap.getSiblingPath(treeId, index);
400
405
 
401
406
  return [new Fr(index), ...siblingPath.toFields()];
402
407
  }
403
408
 
404
- // async getSiblingPath(blockNumber: number, treeId: MerkleTreeId, leafIndex: Fr) {
405
- // const committedDb = new MerkleTreeSnapshotOperationsFacade(this.trees, blockNumber);
406
- // const result = await committedDb.getSiblingPath(treeId, leafIndex.toBigInt());
407
- // return result.toFields();
408
- // }
409
+ async getSiblingPath(blockNumber: number, treeId: MerkleTreeId, leafIndex: Fr) {
410
+ const snap = this.nativeWorldStateService.getSnapshot(blockNumber);
411
+
412
+ const result = await snap.getSiblingPath(treeId, leafIndex.toBigInt());
413
+ return result.toFields();
414
+ }
409
415
 
410
416
  async getNullifierMembershipWitness(
411
417
  blockNumber: number,
412
418
  nullifier: Fr,
413
419
  ): Promise<NullifierMembershipWitness | undefined> {
414
- const db = await this.#getTreesAt(blockNumber);
415
- const index = (await db.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, [nullifier.toBuffer()]))[0];
420
+ const snap = this.nativeWorldStateService.getSnapshot(blockNumber);
421
+
422
+ const [index] = await snap.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, [nullifier.toBuffer()]);
416
423
  if (!index) {
417
424
  return undefined;
418
425
  }
419
426
 
420
- const leafPreimagePromise = db.getLeafPreimage(MerkleTreeId.NULLIFIER_TREE, index);
421
- const siblingPathPromise = db.getSiblingPath<typeof NULLIFIER_TREE_HEIGHT>(
427
+ const leafPreimagePromise = snap.getLeafPreimage(MerkleTreeId.NULLIFIER_TREE, index);
428
+ const siblingPathPromise = snap.getSiblingPath<typeof NULLIFIER_TREE_HEIGHT>(
422
429
  MerkleTreeId.NULLIFIER_TREE,
423
430
  BigInt(index),
424
431
  );
@@ -433,16 +440,17 @@ export class TXE implements TypedOracle {
433
440
  }
434
441
 
435
442
  async getPublicDataTreeWitness(blockNumber: number, leafSlot: Fr): Promise<PublicDataWitness | undefined> {
436
- const db = await this.#getTreesAt(blockNumber);
437
- const lowLeafResult = await db.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot.toBigInt());
443
+ const snap = this.nativeWorldStateService.getSnapshot(blockNumber);
444
+
445
+ const lowLeafResult = await snap.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot.toBigInt());
438
446
  if (!lowLeafResult) {
439
447
  return undefined;
440
448
  } else {
441
- const preimage = (await db.getLeafPreimage(
449
+ const preimage = (await snap.getLeafPreimage(
442
450
  MerkleTreeId.PUBLIC_DATA_TREE,
443
451
  lowLeafResult.index,
444
452
  )) as PublicDataTreeLeafPreimage;
445
- const path = await db.getSiblingPath<typeof PUBLIC_DATA_TREE_HEIGHT>(
453
+ const path = await snap.getSiblingPath<typeof PUBLIC_DATA_TREE_HEIGHT>(
446
454
  MerkleTreeId.PUBLIC_DATA_TREE,
447
455
  lowLeafResult.index,
448
456
  );
@@ -454,8 +462,9 @@ export class TXE implements TypedOracle {
454
462
  blockNumber: number,
455
463
  nullifier: Fr,
456
464
  ): Promise<NullifierMembershipWitness | undefined> {
457
- const committedDb = await this.#getTreesAt(blockNumber);
458
- const findResult = await committedDb.getPreviousValueIndex(MerkleTreeId.NULLIFIER_TREE, nullifier.toBigInt());
465
+ const snap = this.nativeWorldStateService.getSnapshot(blockNumber);
466
+
467
+ const findResult = await snap.getPreviousValueIndex(MerkleTreeId.NULLIFIER_TREE, nullifier.toBigInt());
459
468
  if (!findResult) {
460
469
  return undefined;
461
470
  }
@@ -463,9 +472,9 @@ export class TXE implements TypedOracle {
463
472
  if (alreadyPresent) {
464
473
  this.logger.warn(`Nullifier ${nullifier.toBigInt()} already exists in the tree`);
465
474
  }
466
- const preimageData = (await committedDb.getLeafPreimage(MerkleTreeId.NULLIFIER_TREE, index))!;
475
+ const preimageData = (await snap.getLeafPreimage(MerkleTreeId.NULLIFIER_TREE, index))!;
467
476
 
468
- const siblingPath = await committedDb.getSiblingPath<typeof NULLIFIER_TREE_HEIGHT>(
477
+ const siblingPath = await snap.getSiblingPath<typeof NULLIFIER_TREE_HEIGHT>(
469
478
  MerkleTreeId.NULLIFIER_TREE,
470
479
  BigInt(index),
471
480
  );
@@ -473,10 +482,26 @@ export class TXE implements TypedOracle {
473
482
  }
474
483
 
475
484
  async getBlockHeader(blockNumber: number): Promise<BlockHeader | undefined> {
476
- const header = BlockHeader.empty();
477
- const db = await this.#getTreesAt(blockNumber);
478
- header.state = await db.getStateReference();
479
- header.globalVariables.blockNumber = new Fr(blockNumber);
485
+ if (blockNumber === 1) {
486
+ // TODO: Figure out why native merkle trees cannot get snapshot of 0, as it defaults to latest
487
+ throw new Error('Cannot get the block header of block number 1');
488
+ }
489
+
490
+ const snap = this.nativeWorldStateService.getSnapshot(blockNumber);
491
+ const stateReference = await snap.getStateReference();
492
+
493
+ const previousState = this.nativeWorldStateService.getSnapshot(blockNumber - 1);
494
+ const archiveInfo = await previousState.getTreeInfo(MerkleTreeId.ARCHIVE);
495
+
496
+ const header = new BlockHeader(
497
+ new AppendOnlyTreeSnapshot(new Fr(archiveInfo.root), Number(archiveInfo.size)),
498
+ makeContentCommitment(),
499
+ stateReference,
500
+ makeGlobalVariables(),
501
+ Fr.ZERO,
502
+ Fr.ZERO,
503
+ );
504
+
480
505
  return header;
481
506
  }
482
507
 
@@ -557,21 +582,20 @@ export class TXE implements TypedOracle {
557
582
 
558
583
  async notifyNullifiedNote(innerNullifier: Fr, noteHash: Fr, counter: number) {
559
584
  await this.checkNullifiersNotInTree(this.contractAddress, [innerNullifier]);
560
- this.noteCache.nullifyNote(this.contractAddress, innerNullifier, noteHash);
585
+ await this.noteCache.nullifyNote(this.contractAddress, innerNullifier, noteHash);
561
586
  this.sideEffectCounter = counter + 1;
562
- return Promise.resolve();
563
587
  }
564
588
 
565
589
  async notifyCreatedNullifier(innerNullifier: Fr): Promise<void> {
566
590
  await this.checkNullifiersNotInTree(this.contractAddress, [innerNullifier]);
567
- this.noteCache.nullifierCreated(this.contractAddress, innerNullifier);
568
- return Promise.resolve();
591
+ await this.noteCache.nullifierCreated(this.contractAddress, innerNullifier);
569
592
  }
570
593
 
571
594
  async checkNullifierExists(innerNullifier: Fr): Promise<boolean> {
572
- const nullifier = siloNullifier(this.contractAddress, innerNullifier!);
573
- const db = await this.trees.getLatest();
574
- const index = (await db.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, [nullifier.toBuffer()]))[0];
595
+ const snap = this.nativeWorldStateService.getSnapshot(this.blockNumber - 1);
596
+
597
+ const nullifier = await siloNullifier(this.contractAddress, innerNullifier!);
598
+ const [index] = await snap.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, [nullifier.toBuffer()]);
575
599
  return index !== undefined;
576
600
  }
577
601
 
@@ -589,11 +613,17 @@ export class TXE implements TypedOracle {
589
613
  blockNumber: number,
590
614
  numberOfElements: number,
591
615
  ): Promise<Fr[]> {
592
- const db = await this.#getTreesAt(blockNumber);
616
+ let db: MerkleTreeReadOperations;
617
+ if (blockNumber === this.blockNumber) {
618
+ db = this.baseFork;
619
+ } else {
620
+ db = this.nativeWorldStateService.getSnapshot(blockNumber);
621
+ }
622
+
593
623
  const values = [];
594
624
  for (let i = 0n; i < numberOfElements; i++) {
595
625
  const storageSlot = startStorageSlot.add(new Fr(i));
596
- const leafSlot = computePublicDataTreeLeafSlot(contractAddress, storageSlot).toBigInt();
626
+ const leafSlot = (await computePublicDataTreeLeafSlot(contractAddress, storageSlot)).toBigInt();
597
627
 
598
628
  const lowLeafResult = await db.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot);
599
629
 
@@ -612,18 +642,15 @@ export class TXE implements TypedOracle {
612
642
  }
613
643
 
614
644
  async storageWrite(startStorageSlot: Fr, values: Fr[]): Promise<Fr[]> {
615
- const db = await this.trees.getLatest();
616
-
617
- const publicDataWrites = values.map((value, i) => {
618
- const storageSlot = startStorageSlot.add(new Fr(i));
619
- this.logger.debug(`Oracle storage write: slot=${storageSlot.toString()} value=${value}`);
620
- return new PublicDataTreeLeaf(computePublicDataTreeLeafSlot(this.contractAddress, storageSlot), value);
621
- });
622
- await db.batchInsert(
623
- MerkleTreeId.PUBLIC_DATA_TREE,
624
- publicDataWrites.map(write => write.toBuffer()),
625
- 0,
645
+ const publicDataWrites = await Promise.all(
646
+ values.map(async (value, i) => {
647
+ const storageSlot = startStorageSlot.add(new Fr(i));
648
+ this.logger.debug(`Oracle storage write: slot=${storageSlot.toString()} value=${value}`);
649
+ return new PublicDataWrite(await computePublicDataTreeLeafSlot(this.contractAddress, storageSlot), value);
650
+ }),
626
651
  );
652
+
653
+ await this.addPublicDataWrites(publicDataWrites);
627
654
  return publicDataWrites.map(write => write.value);
628
655
  }
629
656
 
@@ -636,35 +663,91 @@ export class TXE implements TypedOracle {
636
663
  this.committedBlocks.add(blockNumber);
637
664
  }
638
665
 
666
+ const fork = this.baseFork;
667
+
639
668
  const txEffect = TxEffect.empty();
640
669
 
641
670
  const nonceGenerator = usedTxRequestHashForNonces ? this.getTxRequestHash() : this.noteCache.getAllNullifiers()[0];
642
671
 
643
672
  let i = 0;
644
- txEffect.noteHashes = [
645
- ...this.noteCache
673
+ const uniqueNoteHashesFromPrivate = await Promise.all(
674
+ this.noteCache
646
675
  .getAllNotes()
647
- .map(pendingNote =>
676
+ .map(async pendingNote =>
648
677
  computeUniqueNoteHash(
649
- computeNoteHashNonce(nonceGenerator, i++),
650
- siloNoteHash(pendingNote.note.contractAddress, pendingNote.noteHashForConsumption),
678
+ await computeNoteHashNonce(nonceGenerator, i++),
679
+ await siloNoteHash(pendingNote.note.contractAddress, pendingNote.noteHashForConsumption),
651
680
  ),
652
681
  ),
653
- ...this.uniqueNoteHashesFromPublic,
654
- ];
682
+ );
683
+ txEffect.noteHashes = [...uniqueNoteHashesFromPrivate, ...this.uniqueNoteHashesFromPublic];
655
684
 
656
- txEffect.nullifiers = this.noteCache.getAllNullifiers();
685
+ txEffect.nullifiers = [...this.siloedNullifiersFromPublic, ...this.noteCache.getAllNullifiers()];
657
686
  if (usedTxRequestHashForNonces) {
658
687
  txEffect.nullifiers.unshift(this.getTxRequestHash());
659
688
  }
660
- this.node.setTxEffect(blockNumber, new TxHash(new Fr(blockNumber)), txEffect);
689
+
690
+ txEffect.publicDataWrites = this.publicDataWrites;
691
+
692
+ const body = new Body([txEffect]);
693
+
694
+ const l2Block = new L2Block(
695
+ makeAppendOnlyTreeSnapshot(blockNumber + 1),
696
+ makeHeader(0, blockNumber, blockNumber),
697
+ body,
698
+ );
699
+
700
+ const paddedTxEffects = l2Block.body.txEffects;
701
+
702
+ const l1ToL2Messages = Array(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP).fill(0).map(Fr.zero);
703
+
704
+ {
705
+ const noteHashesPadded = paddedTxEffects.flatMap(txEffect =>
706
+ padArrayEnd(txEffect.noteHashes, Fr.ZERO, MAX_NOTE_HASHES_PER_TX),
707
+ );
708
+ await fork.appendLeaves(MerkleTreeId.NOTE_HASH_TREE, noteHashesPadded);
709
+
710
+ await fork.appendLeaves(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, l1ToL2Messages);
711
+ }
712
+
713
+ {
714
+ for (const txEffect of paddedTxEffects) {
715
+ // We do not need to add public data writes because we apply them as we go. We use the sequentialInsert because
716
+ // the batchInsert was not working when updating a previously updated slot.
717
+
718
+ const nullifiersPadded = padArrayEnd(txEffect.nullifiers, Fr.ZERO, MAX_NULLIFIERS_PER_TX);
719
+
720
+ await fork.batchInsert(
721
+ MerkleTreeId.NULLIFIER_TREE,
722
+ nullifiersPadded.map(nullifier => nullifier.toBuffer()),
723
+ NULLIFIER_SUBTREE_HEIGHT,
724
+ );
725
+ }
726
+ }
727
+
728
+ await this.node.setTxEffect(blockNumber, new TxHash(new Fr(blockNumber)), txEffect);
661
729
  this.node.setNullifiersIndexesWithBlock(blockNumber, txEffect.nullifiers);
662
730
  this.node.addNoteLogsByTags(this.blockNumber, this.privateLogs);
663
731
  this.node.addPublicLogsByTags(this.blockNumber, this.publicLogs);
664
732
 
665
- await this.addUniqueNoteHashes(txEffect.noteHashes);
666
- await this.addSiloedNullifiers(txEffect.nullifiers);
733
+ const stateReference = await fork.getStateReference();
734
+ const archiveInfo = await fork.getTreeInfo(MerkleTreeId.ARCHIVE);
735
+ const header = new BlockHeader(
736
+ new AppendOnlyTreeSnapshot(new Fr(archiveInfo.root), Number(archiveInfo.size)),
737
+ makeContentCommitment(),
738
+ stateReference,
739
+ makeGlobalVariables(),
740
+ Fr.ZERO,
741
+ Fr.ZERO,
742
+ );
743
+
744
+ l2Block.header = header;
667
745
 
746
+ await fork.updateArchive(l2Block.header);
747
+
748
+ await this.nativeWorldStateService.handleL2BlockAndMessages(l2Block, l1ToL2Messages);
749
+
750
+ this.publicDataWrites = [];
668
751
  this.privateLogs = [];
669
752
  this.publicLogs = [];
670
753
  this.uniqueNoteHashesFromPublic = [];
@@ -743,7 +826,7 @@ export class TXE implements TypedOracle {
743
826
  const endSideEffectCounter = publicInputs.endSideEffectCounter;
744
827
  this.sideEffectCounter = endSideEffectCounter.toNumber() + 1;
745
828
 
746
- this.addPrivateLogs(
829
+ await this.addPrivateLogs(
747
830
  targetContractAddress,
748
831
  publicInputs.privateLogs.filter(privateLog => !privateLog.isEmpty()).map(privateLog => privateLog.log),
749
832
  );
@@ -787,22 +870,25 @@ export class TXE implements TypedOracle {
787
870
  if (!artifact) {
788
871
  return undefined;
789
872
  }
790
-
791
- const f = artifact.functions.find(f =>
792
- FunctionSelector.fromNameAndParameters(f.name, f.parameters).equals(selector),
873
+ const functionSelectorsAndNames = await Promise.all(
874
+ artifact.functions.map(async f => ({
875
+ name: f.name,
876
+ selector: await FunctionSelector.fromNameAndParameters(f.name, f.parameters),
877
+ })),
793
878
  );
794
- if (!f) {
879
+ const functionSelectorAndName = functionSelectorsAndNames.find(f => f.selector.equals(selector));
880
+ if (!functionSelectorAndName) {
795
881
  return undefined;
796
882
  }
797
883
 
798
- return `${artifact.name}:${f.name}`;
884
+ return `${artifact.name}:${functionSelectorAndName.name}`;
799
885
  }
800
886
 
801
887
  private async executePublicFunction(args: Fr[], callContext: CallContext, isTeardown: boolean = false) {
802
888
  const executionRequest = new PublicExecutionRequest(callContext, args);
803
889
 
804
- const db = await this.trees.getLatest();
805
- const worldStateDb = new TXEWorldStateDB(db, new TXEPublicContractDataSource(this));
890
+ const db = this.baseFork;
891
+ const worldStateDb = new TXEWorldStateDB(db, new TXEPublicContractDataSource(this), this);
806
892
 
807
893
  const globalVariables = GlobalVariables.empty();
808
894
  globalVariables.chainId = new Fr(await this.node.getChainId());
@@ -810,21 +896,28 @@ export class TXE implements TypedOracle {
810
896
  globalVariables.blockNumber = new Fr(this.blockNumber);
811
897
  globalVariables.gasFees = new GasFees(1, 1);
812
898
 
899
+ const tempFork = await this.nativeWorldStateService.fork();
900
+ // Apply current public data writes
901
+ await tempFork.sequentialInsert(
902
+ MerkleTreeId.PUBLIC_DATA_TREE,
903
+ this.publicDataWrites.map(p => p.toBuffer()),
904
+ );
905
+
813
906
  // If the contract instance exists in the TXE's world state, make sure its nullifier is present in the tree
814
907
  // so its nullifier check passes.
815
908
  if ((await worldStateDb.getContractInstance(callContext.contractAddress)) !== undefined) {
816
- const contractAddressNullifier = siloNullifier(
909
+ const contractAddressNullifier = await siloNullifier(
817
910
  AztecAddress.fromNumber(DEPLOYER_CONTRACT_ADDRESS),
818
911
  callContext.contractAddress.toField(),
819
912
  );
820
913
  if ((await worldStateDb.getNullifierIndex(contractAddressNullifier)) === undefined) {
821
- await db.batchInsert(MerkleTreeId.NULLIFIER_TREE, [contractAddressNullifier.toBuffer()], 0);
914
+ await tempFork.batchInsert(MerkleTreeId.NULLIFIER_TREE, [contractAddressNullifier.toBuffer()], 0);
822
915
  }
823
916
  }
824
917
 
825
918
  const simulator = new PublicTxSimulator(
826
- db,
827
- new TXEWorldStateDB(db, new TXEPublicContractDataSource(this)),
919
+ tempFork,
920
+ new TXEWorldStateDB(tempFork, new TXEPublicContractDataSource(this), this),
828
921
  globalVariables,
829
922
  );
830
923
 
@@ -834,25 +927,34 @@ export class TXE implements TypedOracle {
834
927
  // When setting up a teardown call, we tell it that
835
928
  // private execution used Gas(1, 1) so it can compute a tx fee.
836
929
  const gasUsedByPrivate = isTeardown ? new Gas(1, 1) : Gas.empty();
837
- const tx = createTxForPublicCalls(
930
+ const tx = await createTxForPublicCalls(
931
+ firstNullifier,
838
932
  /*setupExecutionRequests=*/ [],
839
933
  /*appExecutionRequests=*/ isTeardown ? [] : [executionRequest],
840
- firstNullifier,
841
934
  /*teardownExecutionRequests=*/ isTeardown ? executionRequest : undefined,
935
+ /*feePayer=*/ AztecAddress.zero(),
842
936
  gasUsedByPrivate,
843
937
  );
844
938
 
845
939
  const result = await simulator.simulate(tx);
846
- const noteHashes = result.avmProvingRequest.inputs.output.accumulatedData.noteHashes.filter(s => !s.isEmpty());
940
+ const noteHashes = result.avmProvingRequest.inputs.publicInputs.accumulatedData.noteHashes.filter(
941
+ s => !s.isEmpty(),
942
+ );
847
943
 
848
- await this.addUniqueNoteHashesFromPublic(noteHashes);
944
+ const publicDataWrites = result.avmProvingRequest.inputs.publicInputs.accumulatedData.publicDataWrites.filter(
945
+ s => !s.isEmpty(),
946
+ );
947
+ await this.addPublicDataWrites(publicDataWrites);
948
+
949
+ this.addUniqueNoteHashesFromPublic(noteHashes);
849
950
 
850
951
  this.addPublicLogs(
851
- result.avmProvingRequest.inputs.output.accumulatedData.publicLogs.filter(
952
+ result.avmProvingRequest.inputs.publicInputs.accumulatedData.publicLogs.filter(
852
953
  log => !log.contractAddress.equals(AztecAddress.ZERO),
853
954
  ),
854
955
  );
855
956
 
957
+ await tempFork.close();
856
958
  return Promise.resolve(result);
857
959
  }
858
960
 
@@ -880,7 +982,7 @@ export class TXE implements TypedOracle {
880
982
  );
881
983
 
882
984
  const args = [this.functionSelector.toField(), ...this.executionCache.getPreimage(argsHash)];
883
- const newArgsHash = this.executionCache.store(args);
985
+ const newArgsHash = await this.executionCache.store(args);
884
986
 
885
987
  const executionResult = await this.executePublicFunction(args, callContext, isTeardown);
886
988
 
@@ -900,17 +1002,14 @@ export class TXE implements TypedOracle {
900
1002
  }
901
1003
 
902
1004
  // Apply side effects
903
- const sideEffects = executionResult.avmProvingRequest.inputs.output.accumulatedData;
904
- const publicDataWrites = sideEffects.publicDataWrites.filter(s => !s.isEmpty());
905
- const noteHashes = sideEffects.noteHashes.filter(s => !s.isEmpty());
1005
+ const sideEffects = executionResult.avmProvingRequest.inputs.publicInputs.accumulatedData;
906
1006
 
907
1007
  const { usedTxRequestHashForNonces } = this.noteCache.finish();
908
1008
  const firstNullifier = usedTxRequestHashForNonces ? this.getTxRequestHash() : this.noteCache.getAllNullifiers()[0];
909
1009
  const nullifiers = sideEffects.nullifiers.filter(s => !s.isEmpty()).filter(s => !s.equals(firstNullifier));
910
1010
 
911
- await this.addPublicDataWrites(publicDataWrites);
912
- await this.addUniqueNoteHashesFromPublic(noteHashes);
913
- await this.addSiloedNullifiers(nullifiers);
1011
+ // For some reason we cannot move this up to 'executePublicFunction'. It gives us an error of trying to modify the same nullifier twice.
1012
+ this.addSiloedNullifiersFromPublic(nullifiers);
914
1013
 
915
1014
  this.setContractAddress(currentContractAddress);
916
1015
  this.setMsgSender(currentMessageSender);
@@ -938,8 +1037,8 @@ export class TXE implements TypedOracle {
938
1037
  );
939
1038
  }
940
1039
 
941
- notifySetMinRevertibleSideEffectCounter(minRevertibleSideEffectCounter: number) {
942
- this.noteCache.setMinRevertibleSideEffectCounter(minRevertibleSideEffectCounter);
1040
+ async notifySetMinRevertibleSideEffectCounter(minRevertibleSideEffectCounter: number) {
1041
+ await this.noteCache.setMinRevertibleSideEffectCounter(minRevertibleSideEffectCounter);
943
1042
  }
944
1043
 
945
1044
  debugLog(message: string, fields: Fr[]): void {
@@ -1018,7 +1117,7 @@ export class TXE implements TypedOracle {
1018
1117
 
1019
1118
  // Apply side effects
1020
1119
  if (executionResult.revertCode.isOK()) {
1021
- const sideEffects = executionResult.avmProvingRequest.inputs.output.accumulatedData;
1120
+ const sideEffects = executionResult.avmProvingRequest.inputs.publicInputs.accumulatedData;
1022
1121
  const publicDataWrites = sideEffects.publicDataWrites.filter(s => !s.isEmpty());
1023
1122
  const noteHashes = sideEffects.noteHashes.filter(s => !s.isEmpty());
1024
1123
  const { usedTxRequestHashForNonces } = this.noteCache.finish();
@@ -1027,8 +1126,8 @@ export class TXE implements TypedOracle {
1027
1126
  : this.noteCache.getAllNullifiers()[0];
1028
1127
  const nullifiers = sideEffects.nullifiers.filter(s => !s.isEmpty()).filter(s => !s.equals(firstNullifier));
1029
1128
  await this.addPublicDataWrites(publicDataWrites);
1030
- await this.addUniqueNoteHashes(noteHashes);
1031
- await this.addSiloedNullifiers(nullifiers);
1129
+ this.addUniqueNoteHashesFromPublic(noteHashes);
1130
+ this.addSiloedNullifiersFromPublic(nullifiers);
1032
1131
  }
1033
1132
 
1034
1133
  this.setContractAddress(currentContractAddress);
@@ -1046,37 +1145,35 @@ export class TXE implements TypedOracle {
1046
1145
  }
1047
1146
 
1048
1147
  async avmOpcodeNullifierExists(innerNullifier: Fr, targetAddress: AztecAddress): Promise<boolean> {
1049
- const nullifier = siloNullifier(targetAddress, innerNullifier!);
1050
- const db = await this.trees.getLatest();
1148
+ const nullifier = await siloNullifier(targetAddress, innerNullifier!);
1149
+ const db = this.baseFork;
1051
1150
  const index = (await db.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, [nullifier.toBuffer()]))[0];
1052
1151
  return index !== undefined;
1053
1152
  }
1054
1153
 
1055
1154
  async avmOpcodeEmitNullifier(nullifier: Fr) {
1056
- const db = await this.trees.getLatest();
1057
- const siloedNullifier = siloNullifier(this.contractAddress, nullifier);
1058
- await db.batchInsert(MerkleTreeId.NULLIFIER_TREE, [siloedNullifier.toBuffer()], NULLIFIER_SUBTREE_HEIGHT);
1155
+ const siloedNullifier = await siloNullifier(this.contractAddress, nullifier);
1156
+ this.addSiloedNullifiersFromPublic([siloedNullifier]);
1157
+
1059
1158
  return Promise.resolve();
1060
1159
  }
1061
1160
 
1161
+ // Doesn't this need to get hashed w/ the nonce ?
1062
1162
  async avmOpcodeEmitNoteHash(noteHash: Fr) {
1063
- const db = await this.trees.getLatest();
1064
- const siloedNoteHash = siloNoteHash(this.contractAddress, noteHash);
1065
- await db.appendLeaves(MerkleTreeId.NOTE_HASH_TREE, [siloedNoteHash]);
1163
+ const siloedNoteHash = await siloNoteHash(this.contractAddress, noteHash);
1164
+ this.addUniqueNoteHashesFromPublic([siloedNoteHash]);
1066
1165
  return Promise.resolve();
1067
1166
  }
1068
1167
 
1069
1168
  async avmOpcodeStorageRead(slot: Fr) {
1070
- const db = await this.trees.getLatest();
1071
-
1072
- const leafSlot = computePublicDataTreeLeafSlot(this.contractAddress, slot);
1169
+ const leafSlot = await computePublicDataTreeLeafSlot(this.contractAddress, slot);
1073
1170
 
1074
- const lowLeafResult = await db.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot.toBigInt());
1171
+ const lowLeafResult = await this.baseFork.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot.toBigInt());
1075
1172
  if (!lowLeafResult || !lowLeafResult.alreadyPresent) {
1076
1173
  return Fr.ZERO;
1077
1174
  }
1078
1175
 
1079
- const preimage = (await db.getLeafPreimage(
1176
+ const preimage = (await this.baseFork.getLeafPreimage(
1080
1177
  MerkleTreeId.PUBLIC_DATA_TREE,
1081
1178
  lowLeafResult.index,
1082
1179
  )) as PublicDataTreeLeafPreimage;