@aztec/txe 0.65.2 → 0.67.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,5 @@
1
1
  import {
2
2
  AuthWitness,
3
- type EncryptedL2NoteLog,
4
3
  MerkleTreeId,
5
4
  Note,
6
5
  type NoteStatus,
@@ -8,17 +7,20 @@ import {
8
7
  PublicDataWitness,
9
8
  PublicExecutionRequest,
10
9
  SimulationError,
10
+ TxEffect,
11
+ TxHash,
11
12
  type UnencryptedL2Log,
12
13
  } from '@aztec/circuit-types';
13
14
  import { type CircuitWitnessGenerationStats } from '@aztec/circuit-types/stats';
14
15
  import {
16
+ BlockHeader,
15
17
  CallContext,
16
18
  type ContractInstance,
17
19
  type ContractInstanceWithAddress,
20
+ DEPLOYER_CONTRACT_ADDRESS,
18
21
  Gas,
19
22
  GasFees,
20
23
  GlobalVariables,
21
- Header,
22
24
  IndexedTaggingSecret,
23
25
  type KeyValidationRequest,
24
26
  type L1_TO_L2_MSG_TREE_HEIGHT,
@@ -29,6 +31,7 @@ import {
29
31
  type PUBLIC_DATA_TREE_HEIGHT,
30
32
  PUBLIC_DISPATCH_SELECTOR,
31
33
  PrivateContextInputs,
34
+ type PrivateLog,
32
35
  PublicDataTreeLeaf,
33
36
  type PublicDataTreeLeafPreimage,
34
37
  type PublicDataWrite,
@@ -38,7 +41,13 @@ import {
38
41
  getContractClassFromArtifact,
39
42
  } from '@aztec/circuits.js';
40
43
  import { Schnorr } from '@aztec/circuits.js/barretenberg';
41
- import { computePublicDataTreeLeafSlot, siloNoteHash, siloNullifier } from '@aztec/circuits.js/hash';
44
+ import {
45
+ computeNoteHashNonce,
46
+ computePublicDataTreeLeafSlot,
47
+ computeUniqueNoteHash,
48
+ siloNoteHash,
49
+ siloNullifier,
50
+ } from '@aztec/circuits.js/hash';
42
51
  import {
43
52
  type ContractArtifact,
44
53
  type FunctionAbi,
@@ -52,10 +61,10 @@ import { Fr } from '@aztec/foundation/fields';
52
61
  import { type Logger, applyStringFormatting } from '@aztec/foundation/log';
53
62
  import { Timer } from '@aztec/foundation/timer';
54
63
  import { type KeyStore } from '@aztec/key-store';
55
- import { ContractDataOracle, enrichPublicSimulationError } from '@aztec/pxe';
64
+ import { ContractDataOracle, SimulatorOracle, enrichPublicSimulationError } from '@aztec/pxe';
56
65
  import {
57
66
  ExecutionError,
58
- type ExecutionNoteCache,
67
+ ExecutionNoteCache,
59
68
  type MessageLoadOracleInputs,
60
69
  type NoteData,
61
70
  Oracle,
@@ -76,6 +85,7 @@ import { createTxForPublicCall } from '@aztec/simulator/public/fixtures';
76
85
  import { NoopTelemetryClient } from '@aztec/telemetry-client/noop';
77
86
  import { MerkleTreeSnapshotOperationsFacade, type MerkleTrees } from '@aztec/world-state';
78
87
 
88
+ import { TXENode } from '../node/txe_node.js';
79
89
  import { type TXEDatabase } from '../util/txe_database.js';
80
90
  import { TXEPublicContractDataSource } from '../util/txe_public_contract_data_source.js';
81
91
  import { TXEWorldStateDB } from '../util/txe_world_state_db.js';
@@ -91,11 +101,19 @@ export class TXE implements TypedOracle {
91
101
  private nestedCallReturndata: Fr[] = [];
92
102
 
93
103
  private contractDataOracle: ContractDataOracle;
104
+ private simulatorOracle: SimulatorOracle;
94
105
 
95
106
  private version: Fr = Fr.ONE;
96
107
  private chainId: Fr = Fr.ONE;
97
108
 
98
- private logsByTags = new Map<string, EncryptedL2NoteLog[]>();
109
+ private uniqueNoteHashesFromPublic: Fr[] = [];
110
+ private siloedNullifiersFromPublic: Fr[] = [];
111
+ private privateLogs: PrivateLog[] = [];
112
+ private publicLogs: UnencryptedL2Log[] = [];
113
+
114
+ private committedBlocks = new Set<number>();
115
+
116
+ private node = new TXENode(this.blockNumber);
99
117
 
100
118
  constructor(
101
119
  private logger: Logger,
@@ -109,6 +127,7 @@ export class TXE implements TypedOracle {
109
127
  this.contractAddress = AztecAddress.random();
110
128
  // Default msg_sender (for entrypoints) is now Fr.max_value rather than 0 addr (see #7190 & #7404)
111
129
  this.msgSender = AztecAddress.fromField(Fr.MAX_FIELD_VALUE);
130
+ this.simulatorOracle = new SimulatorOracle(this.contractDataOracle, txeDatabase, keyStore, this.node);
112
131
  }
113
132
 
114
133
  // Utils
@@ -159,6 +178,7 @@ export class TXE implements TypedOracle {
159
178
 
160
179
  setBlockNumber(blockNumber: number) {
161
180
  this.blockNumber = blockNumber;
181
+ this.node.setBlockNumber(blockNumber);
162
182
  }
163
183
 
164
184
  getTrees() {
@@ -213,7 +233,7 @@ export class TXE implements TypedOracle {
213
233
  }
214
234
 
215
235
  async addAuthWitness(address: AztecAddress, messageHash: Fr) {
216
- const account = this.txeDatabase.getAccount(address);
236
+ const account = await this.txeDatabase.getAccount(address);
217
237
  const privateKey = await this.keyStore.getMasterSecretKey(account.publicKeys.masterIncomingViewingPublicKey);
218
238
  const schnorr = new Schnorr();
219
239
  const signature = schnorr.constructSignature(messageHash.toBuffer(), privateKey).toBuffer();
@@ -239,18 +259,66 @@ export class TXE implements TypedOracle {
239
259
  );
240
260
  }
241
261
 
262
+ async addSiloedNullifiersFromPublic(siloedNullifiers: Fr[]) {
263
+ this.siloedNullifiersFromPublic.push(...siloedNullifiers);
264
+
265
+ await this.addSiloedNullifiers(siloedNullifiers);
266
+ }
267
+
242
268
  async addNullifiers(contractAddress: AztecAddress, nullifiers: Fr[]) {
243
269
  const siloedNullifiers = nullifiers.map(nullifier => siloNullifier(contractAddress, nullifier));
244
270
  await this.addSiloedNullifiers(siloedNullifiers);
245
271
  }
246
272
 
247
- async addSiloedNoteHashes(siloedNoteHashes: Fr[]) {
273
+ async addUniqueNoteHashes(siloedNoteHashes: Fr[]) {
248
274
  const db = await this.trees.getLatest();
249
275
  await db.appendLeaves(MerkleTreeId.NOTE_HASH_TREE, siloedNoteHashes);
250
276
  }
277
+
278
+ async addUniqueNoteHashesFromPublic(siloedNoteHashes: Fr[]) {
279
+ this.uniqueNoteHashesFromPublic.push(...siloedNoteHashes);
280
+ await this.addUniqueNoteHashes(siloedNoteHashes);
281
+ }
282
+
251
283
  async addNoteHashes(contractAddress: AztecAddress, noteHashes: Fr[]) {
252
284
  const siloedNoteHashes = noteHashes.map(noteHash => siloNoteHash(contractAddress, noteHash));
253
- await this.addSiloedNoteHashes(siloedNoteHashes);
285
+
286
+ await this.addUniqueNoteHashes(siloedNoteHashes);
287
+ }
288
+
289
+ addPrivateLogs(contractAddress: AztecAddress, privateLogs: PrivateLog[]) {
290
+ privateLogs.forEach(privateLog => {
291
+ privateLog.fields[0] = poseidon2Hash([contractAddress, privateLog.fields[0]]);
292
+ });
293
+
294
+ this.privateLogs.push(...privateLogs);
295
+ }
296
+
297
+ addPublicLogs(logs: UnencryptedL2Log[]) {
298
+ logs.forEach(log => {
299
+ if (log.data.length < 32 * 33) {
300
+ // TODO remove when #9835 and #9836 are fixed
301
+ this.logger.warn(`Skipping unencrypted log with insufficient data length: ${log.data.length}`);
302
+ return;
303
+ }
304
+ try {
305
+ // TODO remove when #9835 and #9836 are fixed. The partial note logs are emitted as bytes, but encoded as Fields.
306
+ // This means that for every 32 bytes of payload, we only have 1 byte of data.
307
+ // 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.
308
+ const correctedBuffer = Buffer.alloc(32);
309
+ const initialOffset = 32;
310
+ for (let i = 0; i < 32; i++) {
311
+ const byte = Fr.fromBuffer(log.data.subarray(i * 32 + initialOffset, i * 32 + 32 + initialOffset)).toNumber();
312
+ correctedBuffer.writeUInt8(byte, i);
313
+ }
314
+ const tag = new Fr(correctedBuffer);
315
+
316
+ this.logger.verbose(`Found tagged unencrypted log with tag ${tag.toString()} in block ${this.blockNumber}`);
317
+ this.publicLogs.push(log);
318
+ } catch (err) {
319
+ this.logger.warn(`Failed to add tagged log to store: ${err}`);
320
+ }
321
+ });
254
322
  }
255
323
 
256
324
  // TypedOracle
@@ -301,11 +369,13 @@ export class TXE implements TypedOracle {
301
369
 
302
370
  async getMembershipWitness(blockNumber: number, treeId: MerkleTreeId, leafValue: Fr): Promise<Fr[] | undefined> {
303
371
  const db = await this.#getTreesAt(blockNumber);
372
+
304
373
  const index = await db.findLeafIndex(treeId, leafValue.toBuffer());
305
- if (!index) {
374
+ if (index === undefined) {
306
375
  throw new Error(`Leaf value: ${leafValue} not found in ${MerkleTreeId[treeId]} at block ${blockNumber}`);
307
376
  }
308
377
  const siblingPath = await db.getSiblingPath(treeId, index);
378
+
309
379
  return [new Fr(index), ...siblingPath.toFields()];
310
380
  }
311
381
 
@@ -358,15 +428,30 @@ export class TXE implements TypedOracle {
358
428
  }
359
429
  }
360
430
 
361
- getLowNullifierMembershipWitness(
362
- _blockNumber: number,
363
- _nullifier: Fr,
431
+ async getLowNullifierMembershipWitness(
432
+ blockNumber: number,
433
+ nullifier: Fr,
364
434
  ): Promise<NullifierMembershipWitness | undefined> {
365
- throw new Error('Method not implemented.');
435
+ const committedDb = await this.#getTreesAt(blockNumber);
436
+ const findResult = await committedDb.getPreviousValueIndex(MerkleTreeId.NULLIFIER_TREE, nullifier.toBigInt());
437
+ if (!findResult) {
438
+ return undefined;
439
+ }
440
+ const { index, alreadyPresent } = findResult;
441
+ if (alreadyPresent) {
442
+ this.logger.warn(`Nullifier ${nullifier.toBigInt()} already exists in the tree`);
443
+ }
444
+ const preimageData = (await committedDb.getLeafPreimage(MerkleTreeId.NULLIFIER_TREE, index))!;
445
+
446
+ const siblingPath = await committedDb.getSiblingPath<typeof NULLIFIER_TREE_HEIGHT>(
447
+ MerkleTreeId.NULLIFIER_TREE,
448
+ BigInt(index),
449
+ );
450
+ return new NullifierMembershipWitness(BigInt(index), preimageData as NullifierLeafPreimage, siblingPath);
366
451
  }
367
452
 
368
- async getHeader(blockNumber: number): Promise<Header | undefined> {
369
- const header = Header.empty();
453
+ async getBlockHeader(blockNumber: number): Promise<BlockHeader | undefined> {
454
+ const header = BlockHeader.empty();
370
455
  const db = await this.#getTreesAt(blockNumber);
371
456
  header.state = await db.getStateReference();
372
457
  header.globalVariables.blockNumber = new Fr(blockNumber);
@@ -385,7 +470,7 @@ export class TXE implements TypedOracle {
385
470
  throw new Error('Method not implemented.');
386
471
  }
387
472
 
388
- getNotes(
473
+ async getNotes(
389
474
  storageSlot: Fr,
390
475
  numSelects: number,
391
476
  selectByIndexes: number[],
@@ -401,10 +486,12 @@ export class TXE implements TypedOracle {
401
486
  offset: number,
402
487
  _status: NoteStatus,
403
488
  ) {
404
- // Nullified pending notes are already removed from the list.
489
+ const syncedNotes = await this.simulatorOracle.getNotes(this.contractAddress, storageSlot, _status);
405
490
  const pendingNotes = this.noteCache.getNotes(this.contractAddress, storageSlot);
406
491
 
407
- const notes = pickNotes<NoteData>(pendingNotes, {
492
+ const notesToFilter = [...pendingNotes, ...syncedNotes];
493
+
494
+ const notes = pickNotes<NoteData>(notesToFilter, {
408
495
  selects: selectByIndexes.slice(0, numSelects).map((index, i) => ({
409
496
  selector: { index, offset: selectByOffsets[i], length: selectByLengths[i] },
410
497
  value: selectValues[i],
@@ -424,10 +511,10 @@ export class TXE implements TypedOracle {
424
511
  .join(', ')}`,
425
512
  );
426
513
 
427
- return Promise.resolve(notes);
514
+ return notes;
428
515
  }
429
516
 
430
- notifyCreatedNote(storageSlot: Fr, noteTypeId: NoteSelector, noteItems: Fr[], noteHash: Fr, counter: number) {
517
+ notifyCreatedNote(storageSlot: Fr, _noteTypeId: NoteSelector, noteItems: Fr[], noteHash: Fr, counter: number) {
431
518
  const note = new Note(noteItems);
432
519
  this.noteCache.addNewNote(
433
520
  {
@@ -509,19 +596,50 @@ export class TXE implements TypedOracle {
509
596
  return publicDataWrites.map(write => write.value);
510
597
  }
511
598
 
512
- emitEncryptedLog(_contractAddress: AztecAddress, _randomness: Fr, _encryptedNote: Buffer, counter: number): void {
513
- this.sideEffectCounter = counter + 1;
514
- return;
515
- }
516
-
517
- emitEncryptedNoteLog(_noteHashCounter: number, _encryptedNote: Buffer, counter: number): void {
518
- this.sideEffectCounter = counter + 1;
519
- return;
520
- }
599
+ async commitState() {
600
+ const blockNumber = await this.getBlockNumber();
601
+ if (this.committedBlocks.has(blockNumber)) {
602
+ throw new Error('Already committed state');
603
+ } else {
604
+ this.committedBlocks.add(blockNumber);
605
+ }
521
606
 
522
- emitUnencryptedLog(_log: UnencryptedL2Log, counter: number): void {
523
- this.sideEffectCounter = counter + 1;
524
- return;
607
+ const txEffect = TxEffect.empty();
608
+
609
+ let i = 0;
610
+ txEffect.noteHashes = [
611
+ ...this.noteCache
612
+ .getAllNotes()
613
+ .map(pendingNote =>
614
+ computeUniqueNoteHash(
615
+ computeNoteHashNonce(new Fr(this.blockNumber + 6969), i++),
616
+ siloNoteHash(pendingNote.note.contractAddress, pendingNote.noteHashForConsumption),
617
+ ),
618
+ ),
619
+ ...this.uniqueNoteHashesFromPublic,
620
+ ];
621
+ txEffect.nullifiers = [new Fr(blockNumber + 6969), ...this.noteCache.getAllNullifiers()];
622
+
623
+ // Using block number itself, (without adding 6969) gets killed at 1 as it says the slot is already used,
624
+ // it seems like we commit a 1 there to the trees before ? To see what I mean, uncomment these lines below
625
+ // let index = await (await this.trees.getLatest()).findLeafIndex(MerkleTreeId.NULLIFIER_TREE, Fr.ONE.toBuffer());
626
+ // console.log('INDEX OF ONE', index);
627
+ // index = await (await this.trees.getLatest()).findLeafIndex(MerkleTreeId.NULLIFIER_TREE, Fr.random().toBuffer());
628
+ // console.log('INDEX OF RANDOM', index);
629
+
630
+ this.node.setTxEffect(blockNumber, new TxHash(new Fr(blockNumber).toBuffer()), txEffect);
631
+ this.node.setNullifiersIndexesWithBlock(blockNumber, txEffect.nullifiers);
632
+ this.node.addNoteLogsByTags(this.blockNumber, this.privateLogs);
633
+ this.node.addPublicLogsByTags(this.blockNumber, this.publicLogs);
634
+
635
+ await this.addUniqueNoteHashes(txEffect.noteHashes);
636
+ await this.addSiloedNullifiers(txEffect.nullifiers);
637
+
638
+ this.privateLogs = [];
639
+ this.publicLogs = [];
640
+ this.uniqueNoteHashesFromPublic = [];
641
+ this.siloedNullifiersFromPublic = [];
642
+ this.noteCache = new ExecutionNoteCache(new Fr(1));
525
643
  }
526
644
 
527
645
  emitContractClassLog(_log: UnencryptedL2Log, _counter: number): Fr {
@@ -588,14 +706,9 @@ export class TXE implements TypedOracle {
588
706
  const endSideEffectCounter = publicInputs.endSideEffectCounter;
589
707
  this.sideEffectCounter = endSideEffectCounter.toNumber() + 1;
590
708
 
591
- await this.addNullifiers(
592
- targetContractAddress,
593
- publicInputs.nullifiers.filter(nullifier => !nullifier.isEmpty()).map(nullifier => nullifier.value),
594
- );
595
-
596
- await this.addNoteHashes(
709
+ this.addPrivateLogs(
597
710
  targetContractAddress,
598
- publicInputs.noteHashes.filter(noteHash => !noteHash.isEmpty()).map(noteHash => noteHash.value),
711
+ publicInputs.privateLogs.filter(privateLog => !privateLog.isEmpty()).map(privateLog => privateLog.log),
599
712
  );
600
713
 
601
714
  this.setContractAddress(currentContractAddress);
@@ -652,6 +765,7 @@ export class TXE implements TypedOracle {
652
765
  const executionRequest = new PublicExecutionRequest(callContext, args);
653
766
 
654
767
  const db = await this.trees.getLatest();
768
+ const worldStateDb = new TXEWorldStateDB(db, new TXEPublicContractDataSource(this));
655
769
 
656
770
  const globalVariables = GlobalVariables.empty();
657
771
  globalVariables.chainId = this.chainId;
@@ -659,12 +773,23 @@ export class TXE implements TypedOracle {
659
773
  globalVariables.blockNumber = new Fr(this.blockNumber);
660
774
  globalVariables.gasFees = new GasFees(1, 1);
661
775
 
776
+ // If the contract instance exists in the TXE's world state, make sure its nullifier is present in the tree
777
+ // so its nullifier check passes.
778
+ if ((await worldStateDb.getContractInstance(callContext.contractAddress)) !== undefined) {
779
+ const contractAddressNullifier = siloNullifier(
780
+ AztecAddress.fromNumber(DEPLOYER_CONTRACT_ADDRESS),
781
+ callContext.contractAddress.toField(),
782
+ );
783
+ if ((await worldStateDb.getNullifierIndex(contractAddressNullifier)) === undefined) {
784
+ await db.batchInsert(MerkleTreeId.NULLIFIER_TREE, [contractAddressNullifier.toBuffer()], 0);
785
+ }
786
+ }
787
+
662
788
  const simulator = new PublicTxSimulator(
663
789
  db,
664
790
  new TXEWorldStateDB(db, new TXEPublicContractDataSource(this)),
665
791
  new NoopTelemetryClient(),
666
792
  globalVariables,
667
- /*realAvmProvingRequests=*/ false,
668
793
  );
669
794
 
670
795
  // When setting up a teardown call, we tell it that
@@ -673,6 +798,9 @@ export class TXE implements TypedOracle {
673
798
  const tx = createTxForPublicCall(executionRequest, gasUsedByPrivate, isTeardown);
674
799
 
675
800
  const result = await simulator.simulate(tx);
801
+
802
+ this.addPublicLogs(tx.unencryptedLogs.unrollLogs());
803
+
676
804
  return Promise.resolve(result);
677
805
  }
678
806
 
@@ -725,7 +853,7 @@ export class TXE implements TypedOracle {
725
853
  const noteHashes = sideEffects.noteHashes.filter(s => !s.isEmpty());
726
854
  const nullifiers = sideEffects.nullifiers.filter(s => !s.isEmpty());
727
855
  await this.addPublicDataWrites(publicDataWrites);
728
- await this.addSiloedNoteHashes(noteHashes);
856
+ await this.addUniqueNoteHashesFromPublic(noteHashes);
729
857
  await this.addSiloedNullifiers(nullifiers);
730
858
 
731
859
  this.setContractAddress(currentContractAddress);
@@ -759,17 +887,7 @@ export class TXE implements TypedOracle {
759
887
  }
760
888
 
761
889
  debugLog(message: string, fields: Fr[]): void {
762
- this.logger.verbose(`debug_log ${applyStringFormatting(message, fields)}`);
763
- }
764
-
765
- emitEncryptedEventLog(
766
- _contractAddress: AztecAddress,
767
- _randomness: Fr,
768
- _encryptedEvent: Buffer,
769
- counter: number,
770
- ): void {
771
- this.sideEffectCounter = counter + 1;
772
- return;
890
+ this.logger.verbose(`${applyStringFormatting(message, fields)}`, { module: `${this.logger.module}:debug_log` });
773
891
  }
774
892
 
775
893
  async incrementAppTaggingSecretIndexAsSender(sender: AztecAddress, recipient: AztecAddress): Promise<void> {
@@ -793,8 +911,17 @@ export class TXE implements TypedOracle {
793
911
  return siloedSecret;
794
912
  }
795
913
 
796
- syncNotes() {
797
- // TODO: Implement
914
+ async syncNotes() {
915
+ const taggedLogsByRecipient = await this.simulatorOracle.syncTaggedLogs(
916
+ this.contractAddress,
917
+ await this.getBlockNumber(),
918
+ undefined,
919
+ );
920
+
921
+ for (const [recipient, taggedLogs] of taggedLogsByRecipient.entries()) {
922
+ await this.simulatorOracle.processTaggedLogs(taggedLogs, AztecAddress.fromString(recipient));
923
+ }
924
+
798
925
  return Promise.resolve();
799
926
  }
800
927
 
@@ -825,7 +952,7 @@ export class TXE implements TypedOracle {
825
952
  const noteHashes = sideEffects.noteHashes.filter(s => !s.isEmpty());
826
953
  const nullifiers = sideEffects.nullifiers.filter(s => !s.isEmpty());
827
954
  await this.addPublicDataWrites(publicDataWrites);
828
- await this.addSiloedNoteHashes(noteHashes);
955
+ await this.addUniqueNoteHashes(noteHashes);
829
956
  await this.addSiloedNullifiers(nullifiers);
830
957
  }
831
958
 
@@ -1,9 +1,9 @@
1
1
  import { SchnorrAccountContractArtifact } from '@aztec/accounts/schnorr';
2
2
  import { L2Block, MerkleTreeId, SimulationError } from '@aztec/circuit-types';
3
3
  import {
4
+ BlockHeader,
4
5
  Fr,
5
6
  FunctionSelector,
6
- Header,
7
7
  PublicDataTreeLeaf,
8
8
  PublicKeys,
9
9
  computePartialAddress,
@@ -14,7 +14,7 @@ import { type ContractArtifact, NoteSelector } from '@aztec/foundation/abi';
14
14
  import { AztecAddress } from '@aztec/foundation/aztec-address';
15
15
  import { type Logger } from '@aztec/foundation/log';
16
16
  import { KeyStore } from '@aztec/key-store';
17
- import { openTmpStore } from '@aztec/kv-store/utils';
17
+ import { openTmpStore } from '@aztec/kv-store/lmdb';
18
18
  import { getCanonicalProtocolContract, protocolContractNames } from '@aztec/protocol-contracts';
19
19
  import { enrichPublicSimulationError } from '@aztec/pxe';
20
20
  import { ExecutionNoteCache, PackedValuesCache, type TypedOracle } from '@aztec/simulator';
@@ -70,9 +70,12 @@ export class TXEService {
70
70
  const nBlocks = fromSingle(blocks).toNumber();
71
71
  this.logger.debug(`time traveling ${nBlocks} blocks`);
72
72
  const trees = (this.typedOracle as TXE).getTrees();
73
+
74
+ await (this.typedOracle as TXE).commitState();
75
+
73
76
  for (let i = 0; i < nBlocks; i++) {
74
77
  const blockNumber = await this.typedOracle.getBlockNumber();
75
- const header = Header.empty();
78
+ const header = BlockHeader.empty();
76
79
  const l2Block = L2Block.empty();
77
80
  header.state = await trees.getStateReference(true);
78
81
  header.globalVariables.blockNumber = new Fr(blockNumber);
@@ -459,30 +462,6 @@ export class TXEService {
459
462
  return toForeignCallResult([toArray(keyValidationRequest.toFields())]);
460
463
  }
461
464
 
462
- emitEncryptedLog(
463
- _contractAddress: ForeignCallSingle,
464
- _randomness: ForeignCallSingle,
465
- _encryptedLog: ForeignCallSingle,
466
- _counter: ForeignCallSingle,
467
- ) {
468
- // TODO(#8811): Implement
469
- return toForeignCallResult([]);
470
- }
471
-
472
- emitEncryptedNoteLog(
473
- _noteHashCounter: ForeignCallSingle,
474
- _encryptedNote: ForeignCallArray,
475
- _counter: ForeignCallSingle,
476
- ) {
477
- // TODO(#8811): Implement
478
- return toForeignCallResult([]);
479
- }
480
-
481
- emitEncryptedEventLog(_contractAddress: AztecAddress, _randomness: Fr, _encryptedEvent: Buffer, _counter: number) {
482
- // TODO(#8811): Implement
483
- return toForeignCallResult([]);
484
- }
485
-
486
465
  async callPrivateFunction(
487
466
  targetContractAddress: ForeignCallSingle,
488
467
  functionSelector: ForeignCallSingle,
@@ -574,8 +553,8 @@ export class TXEService {
574
553
  return toForeignCallResult([]);
575
554
  }
576
555
 
577
- async getHeader(blockNumber: ForeignCallSingle) {
578
- const header = await this.typedOracle.getHeader(fromSingle(blockNumber).toNumber());
556
+ async getBlockHeader(blockNumber: ForeignCallSingle) {
557
+ const header = await this.typedOracle.getBlockHeader(fromSingle(blockNumber).toNumber());
579
558
  if (!header) {
580
559
  throw new Error(`Block header not found for block ${blockNumber}.`);
581
560
  }
@@ -595,9 +574,14 @@ export class TXEService {
595
574
  return toForeignCallResult([toArray(witness)]);
596
575
  }
597
576
 
598
- emitUnencryptedLog(_contractAddress: ForeignCallSingle, _message: ForeignCallArray, _counter: ForeignCallSingle) {
599
- // TODO(#8811): Implement
600
- return toForeignCallResult([]);
577
+ async getLowNullifierMembershipWitness(blockNumber: ForeignCallSingle, nullifier: ForeignCallSingle) {
578
+ const parsedBlockNumber = fromSingle(blockNumber).toNumber();
579
+
580
+ const witness = await this.typedOracle.getLowNullifierMembershipWitness(parsedBlockNumber, fromSingle(nullifier));
581
+ if (!witness) {
582
+ throw new Error(`Low nullifier witness not found for nullifier ${nullifier} at block ${parsedBlockNumber}.`);
583
+ }
584
+ return toForeignCallResult([toArray(witness.toFields())]);
601
585
  }
602
586
 
603
587
  async getAppTaggingSecretAsSender(sender: ForeignCallSingle, recipient: ForeignCallSingle) {
@@ -690,11 +674,6 @@ export class TXEService {
690
674
  return toForeignCallResult([toSingle(new Fr(blockNumber))]);
691
675
  }
692
676
 
693
- avmOpcodeFunctionSelector() {
694
- const functionSelector = (this.typedOracle as TXE).getFunctionSelector();
695
- return toForeignCallResult([toSingle(functionSelector.toField())]);
696
- }
697
-
698
677
  avmOpcodeIsStaticCall() {
699
678
  const isStaticCall = (this.typedOracle as TXE).getIsStaticCall();
700
679
  return toForeignCallResult([toSingle(new Fr(isStaticCall ? 1 : 0))]);
@@ -1,17 +1,17 @@
1
1
  import { type AztecAddress, CompleteAddress } from '@aztec/circuits.js';
2
- import { type AztecKVStore, type AztecMap } from '@aztec/kv-store';
2
+ import { type AztecAsyncKVStore, type AztecAsyncMap } from '@aztec/kv-store';
3
3
  import { KVPxeDatabase } from '@aztec/pxe';
4
4
 
5
5
  export class TXEDatabase extends KVPxeDatabase {
6
- #accounts: AztecMap<string, Buffer>;
6
+ #accounts: AztecAsyncMap<string, Buffer>;
7
7
 
8
- constructor(db: AztecKVStore) {
8
+ constructor(db: AztecAsyncKVStore) {
9
9
  super(db);
10
10
  this.#accounts = db.openMap('accounts');
11
11
  }
12
12
 
13
- getAccount(key: AztecAddress) {
14
- const completeAddress = this.#accounts.get(key.toString());
13
+ async getAccount(key: AztecAddress) {
14
+ const completeAddress = await this.#accounts.getAsync(key.toString());
15
15
  if (!completeAddress) {
16
16
  throw new Error(`Account not found: ${key.toString()}`);
17
17
  }
@@ -20,5 +20,6 @@ export class TXEDatabase extends KVPxeDatabase {
20
20
 
21
21
  async setAccount(key: AztecAddress, value: CompleteAddress) {
22
22
  await this.#accounts.set(key.toString(), value.toBuffer());
23
+ await this.addCompleteAddress(value);
23
24
  }
24
25
  }