@aztec/txe 0.71.0 → 0.73.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,8 @@ import {
34
42
  type PrivateLog,
35
43
  PublicDataTreeLeaf,
36
44
  type PublicDataTreeLeafPreimage,
37
- type PublicDataWrite,
45
+ PublicDataWrite,
46
+ type PublicLog,
38
47
  computeContractClassId,
39
48
  computeTaggingSecretPoint,
40
49
  deriveKeys,
@@ -48,6 +57,12 @@ import {
48
57
  siloNoteHash,
49
58
  siloNullifier,
50
59
  } from '@aztec/circuits.js/hash';
60
+ import {
61
+ makeAppendOnlyTreeSnapshot,
62
+ makeContentCommitment,
63
+ makeGlobalVariables,
64
+ makeHeader,
65
+ } from '@aztec/circuits.js/testing';
51
66
  import {
52
67
  type ContractArtifact,
53
68
  type FunctionAbi,
@@ -56,6 +71,7 @@ import {
56
71
  countArgumentsSize,
57
72
  } from '@aztec/foundation/abi';
58
73
  import { AztecAddress } from '@aztec/foundation/aztec-address';
74
+ import { padArrayEnd } from '@aztec/foundation/collection';
59
75
  import { poseidon2Hash } from '@aztec/foundation/crypto';
60
76
  import { Fr } from '@aztec/foundation/fields';
61
77
  import { type LogFn, type Logger, applyStringFormatting, createDebugOnlyLogger } from '@aztec/foundation/log';
@@ -84,7 +100,7 @@ import {
84
100
  createSimulationError,
85
101
  resolveAssertionMessageFromError,
86
102
  } from '@aztec/simulator/server';
87
- import { MerkleTreeSnapshotOperationsFacade, type MerkleTrees } from '@aztec/world-state';
103
+ import { type NativeWorldStateService } from '@aztec/world-state';
88
104
 
89
105
  import { TXENode } from '../node/txe_node.js';
90
106
  import { type TXEDatabase } from '../util/txe_database.js';
@@ -92,9 +108,8 @@ import { TXEPublicContractDataSource } from '../util/txe_public_contract_data_so
92
108
  import { TXEWorldStateDB } from '../util/txe_world_state_db.js';
93
109
 
94
110
  export class TXE implements TypedOracle {
95
- private blockNumber = 0;
111
+ private blockNumber = 1;
96
112
  private sideEffectCounter = 0;
97
- private contractAddress: AztecAddress;
98
113
  private msgSender: AztecAddress;
99
114
  private functionSelector = FunctionSelector.fromField(new Fr(0));
100
115
  private isStaticCall = false;
@@ -104,10 +119,11 @@ 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[] = [];
110
- private publicLogs: UnencryptedL2Log[] = [];
126
+ private publicLogs: PublicLog[] = [];
111
127
 
112
128
  private committedBlocks = new Set<number>();
113
129
 
@@ -122,18 +138,19 @@ export class TXE implements TypedOracle {
122
138
 
123
139
  debug: LogFn;
124
140
 
125
- constructor(
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,
146
+ private contractAddress: AztecAddress,
147
+ private nativeWorldStateService: NativeWorldStateService,
148
+ private baseFork: MerkleTreeWriteOperations,
131
149
  ) {
132
150
  this.noteCache = new ExecutionNoteCache(this.getTxRequestHash());
133
151
  this.contractDataOracle = new ContractDataOracle(txeDatabase);
134
- this.contractAddress = AztecAddress.random();
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);
@@ -148,14 +165,33 @@ export class TXE implements TypedOracle {
148
165
  this.debug = createDebugOnlyLogger('aztec:kv-pxe-database');
149
166
  }
150
167
 
168
+ static async create(
169
+ logger: Logger,
170
+ executionCache: HashedValuesCache,
171
+ keyStore: KeyStore,
172
+ txeDatabase: TXEDatabase,
173
+ nativeWorldStateService: NativeWorldStateService,
174
+ baseFork: MerkleTreeWriteOperations,
175
+ ) {
176
+ return new TXE(
177
+ logger,
178
+ executionCache,
179
+ keyStore,
180
+ txeDatabase,
181
+ await AztecAddress.random(),
182
+ nativeWorldStateService,
183
+ baseFork,
184
+ );
185
+ }
186
+
151
187
  // Utils
152
188
 
153
- async #getTreesAt(blockNumber: number) {
154
- const db =
155
- blockNumber === (await this.getBlockNumber())
156
- ? await this.trees.getLatest()
157
- : new MerkleTreeSnapshotOperationsFacade(this.trees, blockNumber);
158
- return db;
189
+ getNativeWorldStateService() {
190
+ return this.nativeWorldStateService;
191
+ }
192
+
193
+ getBaseFork() {
194
+ return this.baseFork;
159
195
  }
160
196
 
161
197
  getChainId(): Promise<Fr> {
@@ -199,10 +235,6 @@ export class TXE implements TypedOracle {
199
235
  this.node.setBlockNumber(blockNumber);
200
236
  }
201
237
 
202
- getTrees() {
203
- return this.trees;
204
- }
205
-
206
238
  getContractDataOracle() {
207
239
  return this.contractDataOracle;
208
240
  }
@@ -220,8 +252,8 @@ export class TXE implements TypedOracle {
220
252
  }
221
253
 
222
254
  async addContractArtifact(artifact: ContractArtifact) {
223
- const contractClass = getContractClassFromArtifact(artifact);
224
- await this.txeDatabase.addContractArtifact(computeContractClassId(contractClass), artifact);
255
+ const contractClass = await getContractClassFromArtifact(artifact);
256
+ await this.txeDatabase.addContractArtifact(await computeContractClassId(contractClass), artifact);
225
257
  }
226
258
 
227
259
  async getPrivateContextInputs(
@@ -229,10 +261,21 @@ export class TXE implements TypedOracle {
229
261
  sideEffectsCounter = this.sideEffectCounter,
230
262
  isStaticCall = false,
231
263
  ) {
232
- const db = await this.#getTreesAt(blockNumber);
233
- 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);
234
277
 
235
- const stateReference = await db.getStateReference();
278
+ const stateReference = await snap.getStateReference();
236
279
  const inputs = PrivateContextInputs.empty();
237
280
  inputs.txContext.chainId = new Fr(await this.node.getChainId());
238
281
  inputs.txContext.version = new Fr(await this.node.getVersion());
@@ -254,32 +297,23 @@ export class TXE implements TypedOracle {
254
297
  const account = await this.txeDatabase.getAccount(address);
255
298
  const privateKey = await this.keyStore.getMasterSecretKey(account.publicKeys.masterIncomingViewingPublicKey);
256
299
  const schnorr = new Schnorr();
257
- const signature = schnorr.constructSignature(messageHash.toBuffer(), privateKey).toBuffer();
258
- const authWitness = new AuthWitness(messageHash, [...signature]);
300
+ const signature = await schnorr.constructSignature(messageHash.toBuffer(), privateKey);
301
+ const authWitness = new AuthWitness(messageHash, [...signature.toBuffer()]);
259
302
  return this.txeDatabase.addAuthWitness(authWitness.requestHash, authWitness.witness);
260
303
  }
261
304
 
262
305
  async addPublicDataWrites(writes: PublicDataWrite[]) {
263
- const db = await this.trees.getLatest();
264
- await db.batchInsert(
306
+ this.publicDataWrites.push(...writes);
307
+
308
+ await this.baseFork.sequentialInsert(
265
309
  MerkleTreeId.PUBLIC_DATA_TREE,
266
310
  writes.map(w => new PublicDataTreeLeaf(w.leafSlot, w.value).toBuffer()),
267
- 0,
268
- );
269
- }
270
-
271
- async addSiloedNullifiers(siloedNullifiers: Fr[]) {
272
- const db = await this.trees.getLatest();
273
- await db.batchInsert(
274
- MerkleTreeId.NULLIFIER_TREE,
275
- siloedNullifiers.map(n => n.toBuffer()),
276
- NULLIFIER_SUBTREE_HEIGHT,
277
311
  );
278
312
  }
279
313
 
280
314
  async checkNullifiersNotInTree(contractAddress: AztecAddress, nullifiers: Fr[]) {
281
- const siloedNullifiers = nullifiers.map(nullifier => siloNullifier(contractAddress, nullifier));
282
- const db = await this.trees.getLatest();
315
+ const siloedNullifiers = await Promise.all(nullifiers.map(nullifier => siloNullifier(contractAddress, nullifier)));
316
+ const db = this.baseFork;
283
317
  const nullifierIndexesInTree = await db.findLeafIndices(
284
318
  MerkleTreeId.NULLIFIER_TREE,
285
319
  siloedNullifiers.map(n => n.toBuffer()),
@@ -289,61 +323,29 @@ export class TXE implements TypedOracle {
289
323
  }
290
324
  }
291
325
 
292
- async addSiloedNullifiersFromPublic(siloedNullifiers: Fr[]) {
326
+ addSiloedNullifiersFromPublic(siloedNullifiers: Fr[]) {
293
327
  this.siloedNullifiersFromPublic.push(...siloedNullifiers);
294
-
295
- await this.addSiloedNullifiers(siloedNullifiers);
296
- }
297
-
298
- async addNullifiers(contractAddress: AztecAddress, nullifiers: Fr[]) {
299
- const siloedNullifiers = nullifiers.map(nullifier => siloNullifier(contractAddress, nullifier));
300
- await this.addSiloedNullifiers(siloedNullifiers);
301
- }
302
-
303
- async addUniqueNoteHashes(siloedNoteHashes: Fr[]) {
304
- const db = await this.trees.getLatest();
305
- await db.appendLeaves(MerkleTreeId.NOTE_HASH_TREE, siloedNoteHashes);
306
328
  }
307
329
 
308
- async addUniqueNoteHashesFromPublic(siloedNoteHashes: Fr[]) {
330
+ addUniqueNoteHashesFromPublic(siloedNoteHashes: Fr[]) {
309
331
  this.uniqueNoteHashesFromPublic.push(...siloedNoteHashes);
310
- await this.addUniqueNoteHashes(siloedNoteHashes);
311
332
  }
312
333
 
313
- async addNoteHashes(contractAddress: AztecAddress, noteHashes: Fr[]) {
314
- const siloedNoteHashes = noteHashes.map(noteHash => siloNoteHash(contractAddress, noteHash));
315
-
316
- await this.addUniqueNoteHashes(siloedNoteHashes);
317
- }
318
-
319
- addPrivateLogs(contractAddress: AztecAddress, privateLogs: PrivateLog[]) {
320
- privateLogs.forEach(privateLog => {
321
- privateLog.fields[0] = poseidon2Hash([contractAddress, privateLog.fields[0]]);
322
- });
334
+ async addPrivateLogs(contractAddress: AztecAddress, privateLogs: PrivateLog[]) {
335
+ for (const privateLog of privateLogs) {
336
+ privateLog.fields[0] = await poseidon2Hash([contractAddress, privateLog.fields[0]]);
337
+ }
323
338
 
324
339
  this.privateLogs.push(...privateLogs);
325
340
  }
326
341
 
327
- addPublicLogs(logs: UnencryptedL2Log[]) {
342
+ addPublicLogs(logs: PublicLog[]) {
328
343
  logs.forEach(log => {
329
- if (log.data.length < 32 * 33) {
330
- // TODO remove when #9835 and #9836 are fixed
331
- this.logger.warn(`Skipping unencrypted log with insufficient data length: ${log.data.length}`);
332
- return;
333
- }
334
344
  try {
335
- // TODO remove when #9835 and #9836 are fixed. The partial note logs are emitted as bytes, but encoded as Fields.
336
- // This means that for every 32 bytes of payload, we only have 1 byte of data.
337
- // Also, the tag is not stored in the first 32 bytes of the log, (that's the length of public fields now) but in the next 32.
338
- const correctedBuffer = Buffer.alloc(32);
339
- const initialOffset = 32;
340
- for (let i = 0; i < 32; i++) {
341
- const byte = Fr.fromBuffer(log.data.subarray(i * 32 + initialOffset, i * 32 + 32 + initialOffset)).toNumber();
342
- correctedBuffer.writeUInt8(byte, i);
343
- }
344
- const tag = new Fr(correctedBuffer);
345
-
346
- this.logger.verbose(`Found tagged unencrypted log with tag ${tag.toString()} in block ${this.blockNumber}`);
345
+ // The first elt stores lengths => tag is in fields[1]
346
+ const tag = log.log[1];
347
+
348
+ this.logger.verbose(`Found tagged public log with tag ${tag.toString()} in block ${this.blockNumber}`);
347
349
  this.publicLogs.push(log);
348
350
  } catch (err) {
349
351
  this.logger.warn(`Failed to add tagged log to store: ${err}`);
@@ -373,12 +375,8 @@ export class TXE implements TypedOracle {
373
375
  return Fr.random();
374
376
  }
375
377
 
376
- storeArrayInExecutionCache(values: Fr[]) {
377
- return Promise.resolve(this.executionCache.store(values));
378
- }
379
-
380
378
  storeInExecutionCache(values: Fr[]) {
381
- return Promise.resolve(this.executionCache.store(values));
379
+ return this.executionCache.store(values);
382
380
  }
383
381
 
384
382
  loadFromExecutionCache(returnsHash: Fr) {
@@ -398,34 +396,36 @@ export class TXE implements TypedOracle {
398
396
  }
399
397
 
400
398
  async getMembershipWitness(blockNumber: number, treeId: MerkleTreeId, leafValue: Fr): Promise<Fr[] | undefined> {
401
- const db = await this.#getTreesAt(blockNumber);
402
- 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];
403
401
  if (index === undefined) {
404
402
  throw new Error(`Leaf value: ${leafValue} not found in ${MerkleTreeId[treeId]} at block ${blockNumber}`);
405
403
  }
406
- const siblingPath = await db.getSiblingPath(treeId, index);
404
+ const siblingPath = await snap.getSiblingPath(treeId, index);
407
405
 
408
406
  return [new Fr(index), ...siblingPath.toFields()];
409
407
  }
410
408
 
411
- // async getSiblingPath(blockNumber: number, treeId: MerkleTreeId, leafIndex: Fr) {
412
- // const committedDb = new MerkleTreeSnapshotOperationsFacade(this.trees, blockNumber);
413
- // const result = await committedDb.getSiblingPath(treeId, leafIndex.toBigInt());
414
- // return result.toFields();
415
- // }
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
+ }
416
415
 
417
416
  async getNullifierMembershipWitness(
418
417
  blockNumber: number,
419
418
  nullifier: Fr,
420
419
  ): Promise<NullifierMembershipWitness | undefined> {
421
- const db = await this.#getTreesAt(blockNumber);
422
- 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()]);
423
423
  if (!index) {
424
424
  return undefined;
425
425
  }
426
426
 
427
- const leafPreimagePromise = db.getLeafPreimage(MerkleTreeId.NULLIFIER_TREE, index);
428
- 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>(
429
429
  MerkleTreeId.NULLIFIER_TREE,
430
430
  BigInt(index),
431
431
  );
@@ -440,16 +440,17 @@ export class TXE implements TypedOracle {
440
440
  }
441
441
 
442
442
  async getPublicDataTreeWitness(blockNumber: number, leafSlot: Fr): Promise<PublicDataWitness | undefined> {
443
- const db = await this.#getTreesAt(blockNumber);
444
- 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());
445
446
  if (!lowLeafResult) {
446
447
  return undefined;
447
448
  } else {
448
- const preimage = (await db.getLeafPreimage(
449
+ const preimage = (await snap.getLeafPreimage(
449
450
  MerkleTreeId.PUBLIC_DATA_TREE,
450
451
  lowLeafResult.index,
451
452
  )) as PublicDataTreeLeafPreimage;
452
- const path = await db.getSiblingPath<typeof PUBLIC_DATA_TREE_HEIGHT>(
453
+ const path = await snap.getSiblingPath<typeof PUBLIC_DATA_TREE_HEIGHT>(
453
454
  MerkleTreeId.PUBLIC_DATA_TREE,
454
455
  lowLeafResult.index,
455
456
  );
@@ -461,8 +462,9 @@ export class TXE implements TypedOracle {
461
462
  blockNumber: number,
462
463
  nullifier: Fr,
463
464
  ): Promise<NullifierMembershipWitness | undefined> {
464
- const committedDb = await this.#getTreesAt(blockNumber);
465
- 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());
466
468
  if (!findResult) {
467
469
  return undefined;
468
470
  }
@@ -470,9 +472,9 @@ export class TXE implements TypedOracle {
470
472
  if (alreadyPresent) {
471
473
  this.logger.warn(`Nullifier ${nullifier.toBigInt()} already exists in the tree`);
472
474
  }
473
- const preimageData = (await committedDb.getLeafPreimage(MerkleTreeId.NULLIFIER_TREE, index))!;
475
+ const preimageData = (await snap.getLeafPreimage(MerkleTreeId.NULLIFIER_TREE, index))!;
474
476
 
475
- const siblingPath = await committedDb.getSiblingPath<typeof NULLIFIER_TREE_HEIGHT>(
477
+ const siblingPath = await snap.getSiblingPath<typeof NULLIFIER_TREE_HEIGHT>(
476
478
  MerkleTreeId.NULLIFIER_TREE,
477
479
  BigInt(index),
478
480
  );
@@ -480,10 +482,26 @@ export class TXE implements TypedOracle {
480
482
  }
481
483
 
482
484
  async getBlockHeader(blockNumber: number): Promise<BlockHeader | undefined> {
483
- const header = BlockHeader.empty();
484
- const db = await this.#getTreesAt(blockNumber);
485
- header.state = await db.getStateReference();
486
- 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
+
487
505
  return header;
488
506
  }
489
507
 
@@ -564,21 +582,20 @@ export class TXE implements TypedOracle {
564
582
 
565
583
  async notifyNullifiedNote(innerNullifier: Fr, noteHash: Fr, counter: number) {
566
584
  await this.checkNullifiersNotInTree(this.contractAddress, [innerNullifier]);
567
- this.noteCache.nullifyNote(this.contractAddress, innerNullifier, noteHash);
585
+ await this.noteCache.nullifyNote(this.contractAddress, innerNullifier, noteHash);
568
586
  this.sideEffectCounter = counter + 1;
569
- return Promise.resolve();
570
587
  }
571
588
 
572
589
  async notifyCreatedNullifier(innerNullifier: Fr): Promise<void> {
573
590
  await this.checkNullifiersNotInTree(this.contractAddress, [innerNullifier]);
574
- this.noteCache.nullifierCreated(this.contractAddress, innerNullifier);
575
- return Promise.resolve();
591
+ await this.noteCache.nullifierCreated(this.contractAddress, innerNullifier);
576
592
  }
577
593
 
578
594
  async checkNullifierExists(innerNullifier: Fr): Promise<boolean> {
579
- const nullifier = siloNullifier(this.contractAddress, innerNullifier!);
580
- const db = await this.trees.getLatest();
581
- 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()]);
582
599
  return index !== undefined;
583
600
  }
584
601
 
@@ -596,11 +613,17 @@ export class TXE implements TypedOracle {
596
613
  blockNumber: number,
597
614
  numberOfElements: number,
598
615
  ): Promise<Fr[]> {
599
- 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
+
600
623
  const values = [];
601
624
  for (let i = 0n; i < numberOfElements; i++) {
602
625
  const storageSlot = startStorageSlot.add(new Fr(i));
603
- const leafSlot = computePublicDataTreeLeafSlot(contractAddress, storageSlot).toBigInt();
626
+ const leafSlot = (await computePublicDataTreeLeafSlot(contractAddress, storageSlot)).toBigInt();
604
627
 
605
628
  const lowLeafResult = await db.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot);
606
629
 
@@ -619,18 +642,15 @@ export class TXE implements TypedOracle {
619
642
  }
620
643
 
621
644
  async storageWrite(startStorageSlot: Fr, values: Fr[]): Promise<Fr[]> {
622
- const db = await this.trees.getLatest();
623
-
624
- const publicDataWrites = values.map((value, i) => {
625
- const storageSlot = startStorageSlot.add(new Fr(i));
626
- this.logger.debug(`Oracle storage write: slot=${storageSlot.toString()} value=${value}`);
627
- return new PublicDataTreeLeaf(computePublicDataTreeLeafSlot(this.contractAddress, storageSlot), value);
628
- });
629
- await db.batchInsert(
630
- MerkleTreeId.PUBLIC_DATA_TREE,
631
- publicDataWrites.map(write => write.toBuffer()),
632
- 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
+ }),
633
651
  );
652
+
653
+ await this.addPublicDataWrites(publicDataWrites);
634
654
  return publicDataWrites.map(write => write.value);
635
655
  }
636
656
 
@@ -643,35 +663,91 @@ export class TXE implements TypedOracle {
643
663
  this.committedBlocks.add(blockNumber);
644
664
  }
645
665
 
666
+ const fork = this.baseFork;
667
+
646
668
  const txEffect = TxEffect.empty();
647
669
 
648
670
  const nonceGenerator = usedTxRequestHashForNonces ? this.getTxRequestHash() : this.noteCache.getAllNullifiers()[0];
649
671
 
650
672
  let i = 0;
651
- txEffect.noteHashes = [
652
- ...this.noteCache
673
+ const uniqueNoteHashesFromPrivate = await Promise.all(
674
+ this.noteCache
653
675
  .getAllNotes()
654
- .map(pendingNote =>
676
+ .map(async pendingNote =>
655
677
  computeUniqueNoteHash(
656
- computeNoteHashNonce(nonceGenerator, i++),
657
- siloNoteHash(pendingNote.note.contractAddress, pendingNote.noteHashForConsumption),
678
+ await computeNoteHashNonce(nonceGenerator, i++),
679
+ await siloNoteHash(pendingNote.note.contractAddress, pendingNote.noteHashForConsumption),
658
680
  ),
659
681
  ),
660
- ...this.uniqueNoteHashesFromPublic,
661
- ];
682
+ );
683
+ txEffect.noteHashes = [...uniqueNoteHashesFromPrivate, ...this.uniqueNoteHashesFromPublic];
662
684
 
663
- txEffect.nullifiers = this.noteCache.getAllNullifiers();
685
+ txEffect.nullifiers = [...this.siloedNullifiersFromPublic, ...this.noteCache.getAllNullifiers()];
664
686
  if (usedTxRequestHashForNonces) {
665
687
  txEffect.nullifiers.unshift(this.getTxRequestHash());
666
688
  }
667
- 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);
668
729
  this.node.setNullifiersIndexesWithBlock(blockNumber, txEffect.nullifiers);
669
730
  this.node.addNoteLogsByTags(this.blockNumber, this.privateLogs);
670
731
  this.node.addPublicLogsByTags(this.blockNumber, this.publicLogs);
671
732
 
672
- await this.addUniqueNoteHashes(txEffect.noteHashes);
673
- 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;
674
745
 
746
+ await fork.updateArchive(l2Block.header);
747
+
748
+ await this.nativeWorldStateService.handleL2BlockAndMessages(l2Block, l1ToL2Messages);
749
+
750
+ this.publicDataWrites = [];
675
751
  this.privateLogs = [];
676
752
  this.publicLogs = [];
677
753
  this.uniqueNoteHashesFromPublic = [];
@@ -750,7 +826,7 @@ export class TXE implements TypedOracle {
750
826
  const endSideEffectCounter = publicInputs.endSideEffectCounter;
751
827
  this.sideEffectCounter = endSideEffectCounter.toNumber() + 1;
752
828
 
753
- this.addPrivateLogs(
829
+ await this.addPrivateLogs(
754
830
  targetContractAddress,
755
831
  publicInputs.privateLogs.filter(privateLog => !privateLog.isEmpty()).map(privateLog => privateLog.log),
756
832
  );
@@ -794,22 +870,25 @@ export class TXE implements TypedOracle {
794
870
  if (!artifact) {
795
871
  return undefined;
796
872
  }
797
-
798
- const f = artifact.functions.find(f =>
799
- 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
+ })),
800
878
  );
801
- if (!f) {
879
+ const functionSelectorAndName = functionSelectorsAndNames.find(f => f.selector.equals(selector));
880
+ if (!functionSelectorAndName) {
802
881
  return undefined;
803
882
  }
804
883
 
805
- return `${artifact.name}:${f.name}`;
884
+ return `${artifact.name}:${functionSelectorAndName.name}`;
806
885
  }
807
886
 
808
887
  private async executePublicFunction(args: Fr[], callContext: CallContext, isTeardown: boolean = false) {
809
888
  const executionRequest = new PublicExecutionRequest(callContext, args);
810
889
 
811
- const db = await this.trees.getLatest();
812
- const worldStateDb = new TXEWorldStateDB(db, new TXEPublicContractDataSource(this));
890
+ const db = this.baseFork;
891
+ const worldStateDb = new TXEWorldStateDB(db, new TXEPublicContractDataSource(this), this);
813
892
 
814
893
  const globalVariables = GlobalVariables.empty();
815
894
  globalVariables.chainId = new Fr(await this.node.getChainId());
@@ -817,38 +896,65 @@ export class TXE implements TypedOracle {
817
896
  globalVariables.blockNumber = new Fr(this.blockNumber);
818
897
  globalVariables.gasFees = new GasFees(1, 1);
819
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
+
820
906
  // If the contract instance exists in the TXE's world state, make sure its nullifier is present in the tree
821
907
  // so its nullifier check passes.
822
908
  if ((await worldStateDb.getContractInstance(callContext.contractAddress)) !== undefined) {
823
- const contractAddressNullifier = siloNullifier(
909
+ const contractAddressNullifier = await siloNullifier(
824
910
  AztecAddress.fromNumber(DEPLOYER_CONTRACT_ADDRESS),
825
911
  callContext.contractAddress.toField(),
826
912
  );
827
913
  if ((await worldStateDb.getNullifierIndex(contractAddressNullifier)) === undefined) {
828
- await db.batchInsert(MerkleTreeId.NULLIFIER_TREE, [contractAddressNullifier.toBuffer()], 0);
914
+ await tempFork.batchInsert(MerkleTreeId.NULLIFIER_TREE, [contractAddressNullifier.toBuffer()], 0);
829
915
  }
830
916
  }
831
917
 
832
918
  const simulator = new PublicTxSimulator(
833
- db,
834
- new TXEWorldStateDB(db, new TXEPublicContractDataSource(this)),
919
+ tempFork,
920
+ new TXEWorldStateDB(tempFork, new TXEPublicContractDataSource(this), this),
835
921
  globalVariables,
836
922
  );
837
923
 
924
+ const { usedTxRequestHashForNonces } = this.noteCache.finish();
925
+ const firstNullifier = usedTxRequestHashForNonces ? this.getTxRequestHash() : this.noteCache.getAllNullifiers()[0];
926
+
838
927
  // When setting up a teardown call, we tell it that
839
928
  // private execution used Gas(1, 1) so it can compute a tx fee.
840
929
  const gasUsedByPrivate = isTeardown ? new Gas(1, 1) : Gas.empty();
841
- const tx = createTxForPublicCalls(
930
+ const tx = await createTxForPublicCalls(
931
+ firstNullifier,
842
932
  /*setupExecutionRequests=*/ [],
843
933
  /*appExecutionRequests=*/ isTeardown ? [] : [executionRequest],
844
934
  /*teardownExecutionRequests=*/ isTeardown ? executionRequest : undefined,
935
+ /*feePayer=*/ AztecAddress.zero(),
845
936
  gasUsedByPrivate,
846
937
  );
847
938
 
848
939
  const result = await simulator.simulate(tx);
940
+ const noteHashes = result.avmProvingRequest.inputs.publicInputs.accumulatedData.noteHashes.filter(
941
+ s => !s.isEmpty(),
942
+ );
943
+
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
- this.addPublicLogs(tx.unencryptedLogs.unrollLogs());
951
+ this.addPublicLogs(
952
+ result.avmProvingRequest.inputs.publicInputs.accumulatedData.publicLogs.filter(
953
+ log => !log.contractAddress.equals(AztecAddress.ZERO),
954
+ ),
955
+ );
851
956
 
957
+ await tempFork.close();
852
958
  return Promise.resolve(result);
853
959
  }
854
960
 
@@ -876,7 +982,7 @@ export class TXE implements TypedOracle {
876
982
  );
877
983
 
878
984
  const args = [this.functionSelector.toField(), ...this.executionCache.getPreimage(argsHash)];
879
- const newArgsHash = this.executionCache.store(args);
985
+ const newArgsHash = await this.executionCache.store(args);
880
986
 
881
987
  const executionResult = await this.executePublicFunction(args, callContext, isTeardown);
882
988
 
@@ -896,13 +1002,14 @@ export class TXE implements TypedOracle {
896
1002
  }
897
1003
 
898
1004
  // Apply side effects
899
- const sideEffects = executionResult.avmProvingRequest.inputs.output.accumulatedData;
900
- const publicDataWrites = sideEffects.publicDataWrites.filter(s => !s.isEmpty());
901
- const noteHashes = sideEffects.noteHashes.filter(s => !s.isEmpty());
902
- const nullifiers = sideEffects.nullifiers.filter(s => !s.isEmpty());
903
- await this.addPublicDataWrites(publicDataWrites);
904
- await this.addUniqueNoteHashesFromPublic(noteHashes);
905
- await this.addSiloedNullifiers(nullifiers);
1005
+ const sideEffects = executionResult.avmProvingRequest.inputs.publicInputs.accumulatedData;
1006
+
1007
+ const { usedTxRequestHashForNonces } = this.noteCache.finish();
1008
+ const firstNullifier = usedTxRequestHashForNonces ? this.getTxRequestHash() : this.noteCache.getAllNullifiers()[0];
1009
+ const nullifiers = sideEffects.nullifiers.filter(s => !s.isEmpty()).filter(s => !s.equals(firstNullifier));
1010
+
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);
906
1013
 
907
1014
  this.setContractAddress(currentContractAddress);
908
1015
  this.setMsgSender(currentMessageSender);
@@ -930,8 +1037,8 @@ export class TXE implements TypedOracle {
930
1037
  );
931
1038
  }
932
1039
 
933
- notifySetMinRevertibleSideEffectCounter(minRevertibleSideEffectCounter: number) {
934
- this.noteCache.setMinRevertibleSideEffectCounter(minRevertibleSideEffectCounter);
1040
+ async notifySetMinRevertibleSideEffectCounter(minRevertibleSideEffectCounter: number) {
1041
+ await this.noteCache.setMinRevertibleSideEffectCounter(minRevertibleSideEffectCounter);
935
1042
  }
936
1043
 
937
1044
  debugLog(message: string, fields: Fr[]): void {
@@ -953,7 +1060,7 @@ export class TXE implements TypedOracle {
953
1060
  async #calculateAppTaggingSecret(contractAddress: AztecAddress, sender: AztecAddress, recipient: AztecAddress) {
954
1061
  const senderCompleteAddress = await this.getCompleteAddress(sender);
955
1062
  const senderIvsk = await this.keyStore.getMasterIncomingViewingSecretKey(sender);
956
- const secretPoint = computeTaggingSecretPoint(senderCompleteAddress, senderIvsk, recipient);
1063
+ const secretPoint = await computeTaggingSecretPoint(senderCompleteAddress, senderIvsk, recipient);
957
1064
  // Silo the secret to the app so it can't be used to track other app's notes
958
1065
  const appSecret = poseidon2Hash([secretPoint.x, secretPoint.y, contractAddress]);
959
1066
  return appSecret;
@@ -1010,13 +1117,17 @@ export class TXE implements TypedOracle {
1010
1117
 
1011
1118
  // Apply side effects
1012
1119
  if (executionResult.revertCode.isOK()) {
1013
- const sideEffects = executionResult.avmProvingRequest.inputs.output.accumulatedData;
1120
+ const sideEffects = executionResult.avmProvingRequest.inputs.publicInputs.accumulatedData;
1014
1121
  const publicDataWrites = sideEffects.publicDataWrites.filter(s => !s.isEmpty());
1015
1122
  const noteHashes = sideEffects.noteHashes.filter(s => !s.isEmpty());
1016
- const nullifiers = sideEffects.nullifiers.filter(s => !s.isEmpty());
1123
+ const { usedTxRequestHashForNonces } = this.noteCache.finish();
1124
+ const firstNullifier = usedTxRequestHashForNonces
1125
+ ? this.getTxRequestHash()
1126
+ : this.noteCache.getAllNullifiers()[0];
1127
+ const nullifiers = sideEffects.nullifiers.filter(s => !s.isEmpty()).filter(s => !s.equals(firstNullifier));
1017
1128
  await this.addPublicDataWrites(publicDataWrites);
1018
- await this.addUniqueNoteHashes(noteHashes);
1019
- await this.addSiloedNullifiers(nullifiers);
1129
+ this.addUniqueNoteHashesFromPublic(noteHashes);
1130
+ this.addSiloedNullifiersFromPublic(nullifiers);
1020
1131
  }
1021
1132
 
1022
1133
  this.setContractAddress(currentContractAddress);
@@ -1034,37 +1145,35 @@ export class TXE implements TypedOracle {
1034
1145
  }
1035
1146
 
1036
1147
  async avmOpcodeNullifierExists(innerNullifier: Fr, targetAddress: AztecAddress): Promise<boolean> {
1037
- const nullifier = siloNullifier(targetAddress, innerNullifier!);
1038
- const db = await this.trees.getLatest();
1148
+ const nullifier = await siloNullifier(targetAddress, innerNullifier!);
1149
+ const db = this.baseFork;
1039
1150
  const index = (await db.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, [nullifier.toBuffer()]))[0];
1040
1151
  return index !== undefined;
1041
1152
  }
1042
1153
 
1043
1154
  async avmOpcodeEmitNullifier(nullifier: Fr) {
1044
- const db = await this.trees.getLatest();
1045
- const siloedNullifier = siloNullifier(this.contractAddress, nullifier);
1046
- await db.batchInsert(MerkleTreeId.NULLIFIER_TREE, [siloedNullifier.toBuffer()], NULLIFIER_SUBTREE_HEIGHT);
1155
+ const siloedNullifier = await siloNullifier(this.contractAddress, nullifier);
1156
+ this.addSiloedNullifiersFromPublic([siloedNullifier]);
1157
+
1047
1158
  return Promise.resolve();
1048
1159
  }
1049
1160
 
1161
+ // Doesn't this need to get hashed w/ the nonce ?
1050
1162
  async avmOpcodeEmitNoteHash(noteHash: Fr) {
1051
- const db = await this.trees.getLatest();
1052
- const siloedNoteHash = siloNoteHash(this.contractAddress, noteHash);
1053
- await db.appendLeaves(MerkleTreeId.NOTE_HASH_TREE, [siloedNoteHash]);
1163
+ const siloedNoteHash = await siloNoteHash(this.contractAddress, noteHash);
1164
+ this.addUniqueNoteHashesFromPublic([siloedNoteHash]);
1054
1165
  return Promise.resolve();
1055
1166
  }
1056
1167
 
1057
1168
  async avmOpcodeStorageRead(slot: Fr) {
1058
- const db = await this.trees.getLatest();
1059
-
1060
- const leafSlot = computePublicDataTreeLeafSlot(this.contractAddress, slot);
1169
+ const leafSlot = await computePublicDataTreeLeafSlot(this.contractAddress, slot);
1061
1170
 
1062
- const lowLeafResult = await db.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot.toBigInt());
1171
+ const lowLeafResult = await this.baseFork.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot.toBigInt());
1063
1172
  if (!lowLeafResult || !lowLeafResult.alreadyPresent) {
1064
1173
  return Fr.ZERO;
1065
1174
  }
1066
1175
 
1067
- const preimage = (await db.getLeafPreimage(
1176
+ const preimage = (await this.baseFork.getLeafPreimage(
1068
1177
  MerkleTreeId.PUBLIC_DATA_TREE,
1069
1178
  lowLeafResult.index,
1070
1179
  )) as PublicDataTreeLeafPreimage;