@aztec/txe 0.69.1 → 0.71.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.
@@ -58,31 +58,32 @@ import {
58
58
  import { AztecAddress } from '@aztec/foundation/aztec-address';
59
59
  import { poseidon2Hash } from '@aztec/foundation/crypto';
60
60
  import { Fr } from '@aztec/foundation/fields';
61
- import { type Logger, applyStringFormatting } from '@aztec/foundation/log';
61
+ import { type LogFn, type Logger, applyStringFormatting, createDebugOnlyLogger } from '@aztec/foundation/log';
62
62
  import { Timer } from '@aztec/foundation/timer';
63
63
  import { type KeyStore } from '@aztec/key-store';
64
64
  import { ContractDataOracle, SimulatorOracle, enrichPublicSimulationError } from '@aztec/pxe';
65
65
  import {
66
- ExecutionError,
67
66
  ExecutionNoteCache,
68
67
  type MessageLoadOracleInputs,
69
68
  type NoteData,
70
69
  Oracle,
71
- type PackedValuesCache,
72
- type PublicTxResult,
73
- PublicTxSimulator,
74
70
  type TypedOracle,
75
- acvm,
76
- createSimulationError,
71
+ WASMSimulator,
77
72
  extractCallStack,
78
73
  extractPrivateCircuitPublicInputs,
79
74
  pickNotes,
80
- resolveAssertionMessageFromError,
81
75
  toACVMWitness,
82
76
  witnessMapToFields,
83
- } from '@aztec/simulator';
84
- import { createTxForPublicCall } from '@aztec/simulator/public/fixtures';
85
- import { NoopTelemetryClient } from '@aztec/telemetry-client/noop';
77
+ } from '@aztec/simulator/client';
78
+ import { createTxForPublicCalls } from '@aztec/simulator/public/fixtures';
79
+ import {
80
+ ExecutionError,
81
+ type HashedValuesCache,
82
+ type PublicTxResult,
83
+ PublicTxSimulator,
84
+ createSimulationError,
85
+ resolveAssertionMessageFromError,
86
+ } from '@aztec/simulator/server';
86
87
  import { MerkleTreeSnapshotOperationsFacade, type MerkleTrees } from '@aztec/world-state';
87
88
 
88
89
  import { TXENode } from '../node/txe_node.js';
@@ -103,32 +104,48 @@ export class TXE implements TypedOracle {
103
104
  private contractDataOracle: ContractDataOracle;
104
105
  private simulatorOracle: SimulatorOracle;
105
106
 
106
- private version: Fr = Fr.ONE;
107
- private chainId: Fr = Fr.ONE;
108
-
109
107
  private uniqueNoteHashesFromPublic: Fr[] = [];
110
108
  private siloedNullifiersFromPublic: Fr[] = [];
111
- private siloedNullifiersFromPrivate: Set<string> = new Set();
112
109
  private privateLogs: PrivateLog[] = [];
113
110
  private publicLogs: UnencryptedL2Log[] = [];
114
111
 
115
112
  private committedBlocks = new Set<number>();
116
113
 
117
- private node = new TXENode(this.blockNumber);
114
+ private VERSION = 1;
115
+ private CHAIN_ID = 1;
116
+
117
+ private node: TXENode;
118
+
119
+ private simulationProvider = new WASMSimulator();
120
+
121
+ private noteCache: ExecutionNoteCache;
122
+
123
+ debug: LogFn;
118
124
 
119
125
  constructor(
120
126
  private logger: Logger,
121
127
  private trees: MerkleTrees,
122
- private packedValuesCache: PackedValuesCache,
123
- private noteCache: ExecutionNoteCache,
128
+ private executionCache: HashedValuesCache,
124
129
  private keyStore: KeyStore,
125
130
  private txeDatabase: TXEDatabase,
126
131
  ) {
132
+ this.noteCache = new ExecutionNoteCache(this.getTxRequestHash());
127
133
  this.contractDataOracle = new ContractDataOracle(txeDatabase);
128
134
  this.contractAddress = AztecAddress.random();
135
+
136
+ this.node = new TXENode(this.blockNumber, this.VERSION, this.CHAIN_ID, this.trees);
137
+
129
138
  // Default msg_sender (for entrypoints) is now Fr.max_value rather than 0 addr (see #7190 & #7404)
130
139
  this.msgSender = AztecAddress.fromField(Fr.MAX_FIELD_VALUE);
131
- this.simulatorOracle = new SimulatorOracle(this.contractDataOracle, txeDatabase, keyStore, this.node);
140
+ this.simulatorOracle = new SimulatorOracle(
141
+ this.contractDataOracle,
142
+ txeDatabase,
143
+ keyStore,
144
+ this.node,
145
+ this.simulationProvider,
146
+ );
147
+
148
+ this.debug = createDebugOnlyLogger('aztec:kv-pxe-database');
132
149
  }
133
150
 
134
151
  // Utils
@@ -141,12 +158,12 @@ export class TXE implements TypedOracle {
141
158
  return db;
142
159
  }
143
160
 
144
- getChainId() {
145
- return Promise.resolve(this.chainId);
161
+ getChainId(): Promise<Fr> {
162
+ return Promise.resolve(this.node.getChainId().then(id => new Fr(id)));
146
163
  }
147
164
 
148
- getVersion() {
149
- return Promise.resolve(this.version);
165
+ getVersion(): Promise<Fr> {
166
+ return Promise.resolve(this.node.getVersion().then(v => new Fr(v)));
150
167
  }
151
168
 
152
169
  getMsgSender() {
@@ -217,8 +234,8 @@ export class TXE implements TypedOracle {
217
234
 
218
235
  const stateReference = await db.getStateReference();
219
236
  const inputs = PrivateContextInputs.empty();
220
- inputs.txContext.chainId = this.chainId;
221
- inputs.txContext.version = this.version;
237
+ inputs.txContext.chainId = new Fr(await this.node.getChainId());
238
+ inputs.txContext.version = new Fr(await this.node.getVersion());
222
239
  inputs.historicalHeader.globalVariables.blockNumber = new Fr(blockNumber);
223
240
  inputs.historicalHeader.state = stateReference;
224
241
  inputs.historicalHeader.lastArchive.root = Fr.fromBuffer(
@@ -260,18 +277,14 @@ export class TXE implements TypedOracle {
260
277
  );
261
278
  }
262
279
 
263
- async addNullifiersFromPrivate(contractAddress: AztecAddress, nullifiers: Fr[]) {
280
+ async checkNullifiersNotInTree(contractAddress: AztecAddress, nullifiers: Fr[]) {
264
281
  const siloedNullifiers = nullifiers.map(nullifier => siloNullifier(contractAddress, nullifier));
265
282
  const db = await this.trees.getLatest();
266
283
  const nullifierIndexesInTree = await db.findLeafIndices(
267
284
  MerkleTreeId.NULLIFIER_TREE,
268
285
  siloedNullifiers.map(n => n.toBuffer()),
269
286
  );
270
- const notInTree = nullifierIndexesInTree.every(index => index === undefined);
271
- const notInCache = siloedNullifiers.every(n => !this.siloedNullifiersFromPrivate.has(n.toString()));
272
- if (notInTree && notInCache) {
273
- siloedNullifiers.forEach(n => this.siloedNullifiersFromPrivate.add(n.toString()));
274
- } else {
287
+ if (nullifierIndexesInTree.some(index => index !== undefined)) {
275
288
  throw new Error(`Rejecting tx for emitting duplicate nullifiers`);
276
289
  }
277
290
  }
@@ -360,16 +373,16 @@ export class TXE implements TypedOracle {
360
373
  return Fr.random();
361
374
  }
362
375
 
363
- packArgumentsArray(args: Fr[]) {
364
- return Promise.resolve(this.packedValuesCache.pack(args));
376
+ storeArrayInExecutionCache(values: Fr[]) {
377
+ return Promise.resolve(this.executionCache.store(values));
365
378
  }
366
379
 
367
- packReturns(returns: Fr[]) {
368
- return Promise.resolve(this.packedValuesCache.pack(returns));
380
+ storeInExecutionCache(values: Fr[]) {
381
+ return Promise.resolve(this.executionCache.store(values));
369
382
  }
370
383
 
371
- unpackReturns(returnsHash: Fr) {
372
- return Promise.resolve(this.packedValuesCache.unpack(returnsHash));
384
+ loadFromExecutionCache(returnsHash: Fr) {
385
+ return Promise.resolve(this.executionCache.getPreimage(returnsHash));
373
386
  }
374
387
 
375
388
  getKeyValidationRequest(pkMHash: Fr): Promise<KeyValidationRequest> {
@@ -395,11 +408,11 @@ export class TXE implements TypedOracle {
395
408
  return [new Fr(index), ...siblingPath.toFields()];
396
409
  }
397
410
 
398
- async getSiblingPath(blockNumber: number, treeId: MerkleTreeId, leafIndex: Fr) {
399
- const committedDb = new MerkleTreeSnapshotOperationsFacade(this.trees, blockNumber);
400
- const result = await committedDb.getSiblingPath(treeId, leafIndex.toBigInt());
401
- return result.toFields();
402
- }
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
+ // }
403
416
 
404
417
  async getNullifierMembershipWitness(
405
418
  blockNumber: number,
@@ -550,12 +563,18 @@ export class TXE implements TypedOracle {
550
563
  }
551
564
 
552
565
  async notifyNullifiedNote(innerNullifier: Fr, noteHash: Fr, counter: number) {
553
- await this.addNullifiersFromPrivate(this.contractAddress, [innerNullifier]);
566
+ await this.checkNullifiersNotInTree(this.contractAddress, [innerNullifier]);
554
567
  this.noteCache.nullifyNote(this.contractAddress, innerNullifier, noteHash);
555
568
  this.sideEffectCounter = counter + 1;
556
569
  return Promise.resolve();
557
570
  }
558
571
 
572
+ async notifyCreatedNullifier(innerNullifier: Fr): Promise<void> {
573
+ await this.checkNullifiersNotInTree(this.contractAddress, [innerNullifier]);
574
+ this.noteCache.nullifierCreated(this.contractAddress, innerNullifier);
575
+ return Promise.resolve();
576
+ }
577
+
559
578
  async checkNullifierExists(innerNullifier: Fr): Promise<boolean> {
560
579
  const nullifier = siloNullifier(this.contractAddress, innerNullifier!);
561
580
  const db = await this.trees.getLatest();
@@ -617,6 +636,7 @@ export class TXE implements TypedOracle {
617
636
 
618
637
  async commitState() {
619
638
  const blockNumber = await this.getBlockNumber();
639
+ const { usedTxRequestHashForNonces } = this.noteCache.finish();
620
640
  if (this.committedBlocks.has(blockNumber)) {
621
641
  throw new Error('Already committed state');
622
642
  } else {
@@ -625,30 +645,25 @@ export class TXE implements TypedOracle {
625
645
 
626
646
  const txEffect = TxEffect.empty();
627
647
 
648
+ const nonceGenerator = usedTxRequestHashForNonces ? this.getTxRequestHash() : this.noteCache.getAllNullifiers()[0];
649
+
628
650
  let i = 0;
629
651
  txEffect.noteHashes = [
630
652
  ...this.noteCache
631
653
  .getAllNotes()
632
654
  .map(pendingNote =>
633
655
  computeUniqueNoteHash(
634
- computeNoteHashNonce(new Fr(this.blockNumber + 6969), i++),
656
+ computeNoteHashNonce(nonceGenerator, i++),
635
657
  siloNoteHash(pendingNote.note.contractAddress, pendingNote.noteHashForConsumption),
636
658
  ),
637
659
  ),
638
660
  ...this.uniqueNoteHashesFromPublic,
639
661
  ];
640
- txEffect.nullifiers = [
641
- new Fr(blockNumber + 6969),
642
- ...Array.from(this.siloedNullifiersFromPrivate).map(n => Fr.fromString(n)),
643
- ];
644
-
645
- // Using block number itself, (without adding 6969) gets killed at 1 as it says the slot is already used,
646
- // it seems like we commit a 1 there to the trees before ? To see what I mean, uncomment these lines below
647
- // let index = await (await this.trees.getLatest()).findLeafIndex(MerkleTreeId.NULLIFIER_TREE, Fr.ONE.toBuffer());
648
- // console.log('INDEX OF ONE', index);
649
- // index = await (await this.trees.getLatest()).findLeafIndex(MerkleTreeId.NULLIFIER_TREE, Fr.random().toBuffer());
650
- // console.log('INDEX OF RANDOM', index);
651
662
 
663
+ txEffect.nullifiers = this.noteCache.getAllNullifiers();
664
+ if (usedTxRequestHashForNonces) {
665
+ txEffect.nullifiers.unshift(this.getTxRequestHash());
666
+ }
652
667
  this.node.setTxEffect(blockNumber, new TxHash(new Fr(blockNumber)), txEffect);
653
668
  this.node.setNullifiersIndexesWithBlock(blockNumber, txEffect.nullifiers);
654
669
  this.node.addNoteLogsByTags(this.blockNumber, this.privateLogs);
@@ -659,10 +674,14 @@ export class TXE implements TypedOracle {
659
674
 
660
675
  this.privateLogs = [];
661
676
  this.publicLogs = [];
662
- this.siloedNullifiersFromPrivate = new Set();
663
677
  this.uniqueNoteHashesFromPublic = [];
664
678
  this.siloedNullifiersFromPublic = [];
665
- this.noteCache = new ExecutionNoteCache(new Fr(1));
679
+ this.noteCache = new ExecutionNoteCache(this.getTxRequestHash());
680
+ }
681
+
682
+ getTxRequestHash() {
683
+ // Using block number itself is invalid since indexed trees come prefilled with the first slots.
684
+ return new Fr(this.blockNumber + 6969);
666
685
  }
667
686
 
668
687
  emitContractClassLog(_log: UnencryptedL2Log, _counter: number): Fr {
@@ -697,21 +716,23 @@ export class TXE implements TypedOracle {
697
716
  const initialWitness = await this.getInitialWitness(artifact, argsHash, sideEffectCounter, isStaticCall);
698
717
  const acvmCallback = new Oracle(this);
699
718
  const timer = new Timer();
700
- const acirExecutionResult = await acvm(acir, initialWitness, acvmCallback).catch((err: Error) => {
701
- err.message = resolveAssertionMessageFromError(err, artifact);
702
-
703
- const execError = new ExecutionError(
704
- err.message,
705
- {
706
- contractAddress: targetContractAddress,
707
- functionSelector,
708
- },
709
- extractCallStack(err, artifact.debug),
710
- { cause: err },
711
- );
712
- this.logger.debug(`Error executing private function ${targetContractAddress}:${functionSelector}`);
713
- throw createSimulationError(execError);
714
- });
719
+ const acirExecutionResult = await this.simulationProvider
720
+ .executeUserCircuit(acir, initialWitness, acvmCallback)
721
+ .catch((err: Error) => {
722
+ err.message = resolveAssertionMessageFromError(err, artifact);
723
+
724
+ const execError = new ExecutionError(
725
+ err.message,
726
+ {
727
+ contractAddress: targetContractAddress,
728
+ functionSelector,
729
+ },
730
+ extractCallStack(err, artifact.debug),
731
+ { cause: err },
732
+ );
733
+ this.logger.debug(`Error executing private function ${targetContractAddress}:${functionSelector}`);
734
+ throw createSimulationError(execError);
735
+ });
715
736
  const duration = timer.ms();
716
737
  const publicInputs = extractPrivateCircuitPublicInputs(artifact, acirExecutionResult.partialWitness);
717
738
 
@@ -734,24 +755,6 @@ export class TXE implements TypedOracle {
734
755
  publicInputs.privateLogs.filter(privateLog => !privateLog.isEmpty()).map(privateLog => privateLog.log),
735
756
  );
736
757
 
737
- const executionNullifiers = publicInputs.nullifiers
738
- .filter(nullifier => !nullifier.isEmpty())
739
- .map(nullifier => nullifier.value);
740
- // We inject nullifiers into siloedNullifiersFromPrivate from notifyNullifiedNote,
741
- // so top level calls to destroyNote work as expected. As such, we are certain
742
- // that we would insert duplicates if we just took the nullifiers from the public inputs and
743
- // blindly inserted them into siloedNullifiersFromPrivate. To avoid this, we extract the first
744
- // (and only the first!) duplicated nullifier from the public inputs, so we can just push
745
- // the ones that were not created by deleting a note
746
- const firstDuplicateIndexes = executionNullifiers
747
- .map((nullifier, index) => {
748
- const siloedNullifier = siloNullifier(targetContractAddress, nullifier);
749
- return this.siloedNullifiersFromPrivate.has(siloedNullifier.toString()) ? index : -1;
750
- })
751
- .filter(index => index !== -1);
752
- const nonNoteNullifiers = executionNullifiers.filter((_, index) => !firstDuplicateIndexes.includes(index));
753
- await this.addNullifiersFromPrivate(targetContractAddress, nonNoteNullifiers);
754
-
755
758
  this.setContractAddress(currentContractAddress);
756
759
  this.setMsgSender(currentMessageSender);
757
760
  this.setFunctionSelector(currentFunctionSelector);
@@ -762,7 +765,7 @@ export class TXE implements TypedOracle {
762
765
  async getInitialWitness(abi: FunctionAbi, argsHash: Fr, sideEffectCounter: number, isStaticCall: boolean) {
763
766
  const argumentsSize = countArgumentsSize(abi);
764
767
 
765
- const args = this.packedValuesCache.unpack(argsHash);
768
+ const args = this.executionCache.getPreimage(argsHash);
766
769
 
767
770
  if (args.length !== argumentsSize) {
768
771
  throw new Error('Invalid arguments size');
@@ -809,8 +812,8 @@ export class TXE implements TypedOracle {
809
812
  const worldStateDb = new TXEWorldStateDB(db, new TXEPublicContractDataSource(this));
810
813
 
811
814
  const globalVariables = GlobalVariables.empty();
812
- globalVariables.chainId = this.chainId;
813
- globalVariables.version = this.version;
815
+ globalVariables.chainId = new Fr(await this.node.getChainId());
816
+ globalVariables.version = new Fr(await this.node.getVersion());
814
817
  globalVariables.blockNumber = new Fr(this.blockNumber);
815
818
  globalVariables.gasFees = new GasFees(1, 1);
816
819
 
@@ -829,14 +832,18 @@ export class TXE implements TypedOracle {
829
832
  const simulator = new PublicTxSimulator(
830
833
  db,
831
834
  new TXEWorldStateDB(db, new TXEPublicContractDataSource(this)),
832
- new NoopTelemetryClient(),
833
835
  globalVariables,
834
836
  );
835
837
 
836
838
  // When setting up a teardown call, we tell it that
837
839
  // private execution used Gas(1, 1) so it can compute a tx fee.
838
840
  const gasUsedByPrivate = isTeardown ? new Gas(1, 1) : Gas.empty();
839
- const tx = createTxForPublicCall(executionRequest, gasUsedByPrivate, isTeardown);
841
+ const tx = createTxForPublicCalls(
842
+ /*setupExecutionRequests=*/ [],
843
+ /*appExecutionRequests=*/ isTeardown ? [] : [executionRequest],
844
+ /*teardownExecutionRequests=*/ isTeardown ? executionRequest : undefined,
845
+ gasUsedByPrivate,
846
+ );
840
847
 
841
848
  const result = await simulator.simulate(tx);
842
849
 
@@ -868,8 +875,8 @@ export class TXE implements TypedOracle {
868
875
  isStaticCall,
869
876
  );
870
877
 
871
- const args = [this.functionSelector.toField(), ...this.packedValuesCache.unpack(argsHash)];
872
- const newArgsHash = this.packedValuesCache.pack(args);
878
+ const args = [this.functionSelector.toField(), ...this.executionCache.getPreimage(argsHash)];
879
+ const newArgsHash = this.executionCache.store(args);
873
880
 
874
881
  const executionResult = await this.executePublicFunction(args, callContext, isTeardown);
875
882
 
@@ -968,6 +975,19 @@ export class TXE implements TypedOracle {
968
975
  return Promise.resolve();
969
976
  }
970
977
 
978
+ deliverNote(
979
+ _contractAddress: AztecAddress,
980
+ _storageSlot: Fr,
981
+ _nonce: Fr,
982
+ _content: Fr[],
983
+ _noteHash: Fr,
984
+ _nullifier: Fr,
985
+ _txHash: Fr,
986
+ _recipient: AztecAddress,
987
+ ): Promise<void> {
988
+ throw new Error('deliverNote');
989
+ }
990
+
971
991
  // AVM oracles
972
992
 
973
993
  async avmOpcodeCall(targetContractAddress: AztecAddress, args: Fr[], isStaticCall: boolean): Promise<PublicTxResult> {
@@ -1051,4 +1071,36 @@ export class TXE implements TypedOracle {
1051
1071
 
1052
1072
  return preimage.value;
1053
1073
  }
1074
+
1075
+ dbStore(contractAddress: AztecAddress, slot: Fr, values: Fr[]): Promise<void> {
1076
+ if (!contractAddress.equals(this.contractAddress)) {
1077
+ // TODO(#10727): instead of this check that this.contractAddress is allowed to access the external DB
1078
+ throw new Error(`Contract ${contractAddress} is not allowed to access ${this.contractAddress}'s PXE DB`);
1079
+ }
1080
+ return this.txeDatabase.dbStore(this.contractAddress, slot, values);
1081
+ }
1082
+
1083
+ dbLoad(contractAddress: AztecAddress, slot: Fr): Promise<Fr[] | null> {
1084
+ if (!contractAddress.equals(this.contractAddress)) {
1085
+ // TODO(#10727): instead of this check that this.contractAddress is allowed to access the external DB
1086
+ throw new Error(`Contract ${contractAddress} is not allowed to access ${this.contractAddress}'s PXE DB`);
1087
+ }
1088
+ return this.txeDatabase.dbLoad(this.contractAddress, slot);
1089
+ }
1090
+
1091
+ dbDelete(contractAddress: AztecAddress, slot: Fr): Promise<void> {
1092
+ if (!contractAddress.equals(this.contractAddress)) {
1093
+ // TODO(#10727): instead of this check that this.contractAddress is allowed to access the external DB
1094
+ throw new Error(`Contract ${contractAddress} is not allowed to access ${this.contractAddress}'s PXE DB`);
1095
+ }
1096
+ return this.txeDatabase.dbDelete(this.contractAddress, slot);
1097
+ }
1098
+
1099
+ dbCopy(contractAddress: AztecAddress, srcSlot: Fr, dstSlot: Fr, numEntries: number): Promise<void> {
1100
+ if (!contractAddress.equals(this.contractAddress)) {
1101
+ // TODO(#10727): instead of this check that this.contractAddress is allowed to access the external DB
1102
+ throw new Error(`Contract ${contractAddress} is not allowed to access ${this.contractAddress}'s PXE DB`);
1103
+ }
1104
+ return this.txeDatabase.dbCopy(this.contractAddress, srcSlot, dstSlot, numEntries);
1105
+ }
1054
1106
  }
@@ -18,8 +18,9 @@ import { openTmpStore } from '@aztec/kv-store/lmdb';
18
18
  import { protocolContractNames } from '@aztec/protocol-contracts';
19
19
  import { getCanonicalProtocolContract } from '@aztec/protocol-contracts/bundle';
20
20
  import { enrichPublicSimulationError } from '@aztec/pxe';
21
- import { ExecutionNoteCache, PackedValuesCache, type TypedOracle } from '@aztec/simulator';
22
- import { NoopTelemetryClient } from '@aztec/telemetry-client/noop';
21
+ import { type TypedOracle } from '@aztec/simulator/client';
22
+ import { HashedValuesCache } from '@aztec/simulator/server';
23
+ import { getTelemetryClient } from '@aztec/telemetry-client';
23
24
  import { MerkleTrees } from '@aztec/world-state';
24
25
 
25
26
  import { TXE } from '../oracle/txe_oracle.js';
@@ -41,10 +42,8 @@ export class TXEService {
41
42
 
42
43
  static async init(logger: Logger) {
43
44
  const store = openTmpStore(true);
44
- const trees = await MerkleTrees.new(store, new NoopTelemetryClient(), logger);
45
- const packedValuesCache = new PackedValuesCache();
46
- const txHash = new Fr(1); // The txHash is used for computing the revertible nullifiers for non-revertible note hashes. It can be any value for testing.
47
- const noteCache = new ExecutionNoteCache(txHash);
45
+ const trees = await MerkleTrees.new(store, getTelemetryClient(), logger);
46
+ const executionCache = new HashedValuesCache();
48
47
  const keyStore = new KeyStore(store);
49
48
  const txeDatabase = new TXEDatabase(store);
50
49
  // Register protocol contracts.
@@ -54,7 +53,7 @@ export class TXEService {
54
53
  await txeDatabase.addContractInstance(instance);
55
54
  }
56
55
  logger.debug(`TXE service initialized`);
57
- const txe = new TXE(logger, trees, packedValuesCache, noteCache, keyStore, txeDatabase);
56
+ const txe = new TXE(logger, trees, executionCache, keyStore, txeDatabase);
58
57
  const service = new TXEService(logger, txe);
59
58
  await service.advanceBlocksBy(toSingle(new Fr(1n)));
60
59
  return service;
@@ -272,25 +271,20 @@ export class TXEService {
272
271
  return toForeignCallResult([toSingle(new Fr(blockNumber))]);
273
272
  }
274
273
 
275
- async packArgumentsArray(args: ForeignCallArray) {
276
- const packed = await this.typedOracle.packArgumentsArray(fromArray(args));
277
- return toForeignCallResult([toSingle(packed)]);
278
- }
279
-
280
- async packArguments(_length: ForeignCallSingle, values: ForeignCallArray) {
281
- const packed = await this.typedOracle.packArgumentsArray(fromArray(values));
282
- return toForeignCallResult([toSingle(packed)]);
274
+ async storeArrayInExecutionCache(args: ForeignCallArray) {
275
+ const hash = await this.typedOracle.storeArrayInExecutionCache(fromArray(args));
276
+ return toForeignCallResult([toSingle(hash)]);
283
277
  }
284
278
 
285
279
  // Since the argument is a slice, noir automatically adds a length field to oracle call.
286
- async packReturns(_length: ForeignCallSingle, values: ForeignCallArray) {
287
- const packed = await this.typedOracle.packReturns(fromArray(values));
288
- return toForeignCallResult([toSingle(packed)]);
280
+ async storeInExecutionCache(_length: ForeignCallSingle, values: ForeignCallArray) {
281
+ const returnsHash = await this.typedOracle.storeInExecutionCache(fromArray(values));
282
+ return toForeignCallResult([toSingle(returnsHash)]);
289
283
  }
290
284
 
291
- async unpackReturns(returnsHash: ForeignCallSingle) {
292
- const unpacked = await this.typedOracle.unpackReturns(fromSingle(returnsHash));
293
- return toForeignCallResult([toArray(unpacked)]);
285
+ async loadFromExecutionCache(hash: ForeignCallSingle) {
286
+ const returns = await this.typedOracle.loadFromExecutionCache(fromSingle(hash));
287
+ return toForeignCallResult([toArray(returns)]);
294
288
  }
295
289
 
296
290
  // Since the argument is a slice, noir automatically adds a length field to oracle call.
@@ -334,15 +328,6 @@ export class TXEService {
334
328
  return toForeignCallResult([toArray(witness.toFields())]);
335
329
  }
336
330
 
337
- async getSiblingPath(blockNumber: ForeignCallSingle, treeId: ForeignCallSingle, leafIndex: ForeignCallSingle) {
338
- const result = await this.typedOracle.getSiblingPath(
339
- fromSingle(blockNumber).toNumber(),
340
- fromSingle(treeId).toNumber(),
341
- fromSingle(leafIndex),
342
- );
343
- return toForeignCallResult([toArray(result)]);
344
- }
345
-
346
331
  async getNotes(
347
332
  storageSlot: ForeignCallSingle,
348
333
  numSelects: ForeignCallSingle,
@@ -434,6 +419,11 @@ export class TXEService {
434
419
  return toForeignCallResult([toSingle(new Fr(0))]);
435
420
  }
436
421
 
422
+ async notifyCreatedNullifier(innerNullifier: ForeignCallSingle) {
423
+ await this.typedOracle.notifyCreatedNullifier(fromSingle(innerNullifier));
424
+ return toForeignCallResult([toSingle(new Fr(0))]);
425
+ }
426
+
437
427
  async checkNullifierExists(innerNullifier: ForeignCallSingle) {
438
428
  const exists = await this.typedOracle.checkNullifierExists(fromSingle(innerNullifier));
439
429
  return toForeignCallResult([toSingle(new Fr(exists))]);
@@ -598,6 +588,49 @@ export class TXEService {
598
588
  return toForeignCallResult([]);
599
589
  }
600
590
 
591
+ async dbStore(contractAddress: ForeignCallSingle, slot: ForeignCallSingle, values: ForeignCallArray) {
592
+ await this.typedOracle.dbStore(
593
+ AztecAddress.fromField(fromSingle(contractAddress)),
594
+ fromSingle(slot),
595
+ fromArray(values),
596
+ );
597
+ return toForeignCallResult([]);
598
+ }
599
+
600
+ async dbLoad(contractAddress: ForeignCallSingle, slot: ForeignCallSingle, tSize: ForeignCallSingle) {
601
+ const values = await this.typedOracle.dbLoad(AztecAddress.fromField(fromSingle(contractAddress)), fromSingle(slot));
602
+ // We are going to return a Noir Option struct to represent the possibility of null values. Options are a struct
603
+ // with two fields: `some` (a boolean) and `value` (a field array in this case).
604
+ if (values === null) {
605
+ // No data was found so we set `some` to 0 and pad `value` with zeros get the correct return size.
606
+ return toForeignCallResult([toSingle(new Fr(0)), toArray(Array(fromSingle(tSize).toNumber()).fill(new Fr(0)))]);
607
+ } else {
608
+ // Data was found so we set `some` to 1 and return it along with `value`.
609
+ return toForeignCallResult([toSingle(new Fr(1)), toArray(values)]);
610
+ }
611
+ }
612
+
613
+ async dbDelete(contractAddress: ForeignCallSingle, slot: ForeignCallSingle) {
614
+ await this.typedOracle.dbDelete(AztecAddress.fromField(fromSingle(contractAddress)), fromSingle(slot));
615
+ return toForeignCallResult([]);
616
+ }
617
+
618
+ async dbCopy(
619
+ contractAddress: ForeignCallSingle,
620
+ srcSlot: ForeignCallSingle,
621
+ dstSlot: ForeignCallSingle,
622
+ numEntries: ForeignCallSingle,
623
+ ) {
624
+ await this.typedOracle.dbCopy(
625
+ AztecAddress.fromField(fromSingle(contractAddress)),
626
+ fromSingle(srcSlot),
627
+ fromSingle(dstSlot),
628
+ fromSingle(numEntries).toNumber(),
629
+ );
630
+
631
+ return toForeignCallResult([]);
632
+ }
633
+
601
634
  // AVM opcodes
602
635
 
603
636
  avmOpcodeEmitUnencryptedLog(_message: ForeignCallArray) {
@@ -7,7 +7,7 @@ import {
7
7
  type PublicDataTreeLeafPreimage,
8
8
  } from '@aztec/circuits.js';
9
9
  import { computePublicDataTreeLeafSlot } from '@aztec/circuits.js/hash';
10
- import { WorldStateDB } from '@aztec/simulator';
10
+ import { WorldStateDB } from '@aztec/simulator/server';
11
11
 
12
12
  export class TXEWorldStateDB extends WorldStateDB {
13
13
  constructor(private merkleDb: MerkleTreeWriteOperations, dataSource: ContractDataSource) {