@aztec/txe 0.0.1-commit.c80b6263 → 0.0.1-commit.c949de6bc

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.
Files changed (48) hide show
  1. package/dest/index.d.ts +1 -1
  2. package/dest/index.d.ts.map +1 -1
  3. package/dest/index.js +82 -50
  4. package/dest/oracle/interfaces.d.ts +4 -3
  5. package/dest/oracle/interfaces.d.ts.map +1 -1
  6. package/dest/oracle/txe_oracle_top_level_context.d.ts +7 -8
  7. package/dest/oracle/txe_oracle_top_level_context.d.ts.map +1 -1
  8. package/dest/oracle/txe_oracle_top_level_context.js +99 -30
  9. package/dest/rpc_translator.d.ts +8 -8
  10. package/dest/rpc_translator.d.ts.map +1 -1
  11. package/dest/rpc_translator.js +53 -29
  12. package/dest/state_machine/archiver.d.ts +1 -1
  13. package/dest/state_machine/archiver.d.ts.map +1 -1
  14. package/dest/state_machine/archiver.js +2 -0
  15. package/dest/state_machine/dummy_p2p_client.d.ts +16 -12
  16. package/dest/state_machine/dummy_p2p_client.d.ts.map +1 -1
  17. package/dest/state_machine/dummy_p2p_client.js +28 -16
  18. package/dest/state_machine/index.d.ts +5 -5
  19. package/dest/state_machine/index.d.ts.map +1 -1
  20. package/dest/state_machine/index.js +15 -10
  21. package/dest/state_machine/mock_epoch_cache.d.ts +3 -1
  22. package/dest/state_machine/mock_epoch_cache.d.ts.map +1 -1
  23. package/dest/state_machine/mock_epoch_cache.js +4 -0
  24. package/dest/txe_session.d.ts +9 -6
  25. package/dest/txe_session.d.ts.map +1 -1
  26. package/dest/txe_session.js +80 -21
  27. package/dest/util/txe_public_contract_data_source.d.ts +2 -3
  28. package/dest/util/txe_public_contract_data_source.d.ts.map +1 -1
  29. package/dest/util/txe_public_contract_data_source.js +5 -22
  30. package/dest/utils/block_creation.d.ts +1 -1
  31. package/dest/utils/block_creation.d.ts.map +1 -1
  32. package/dest/utils/block_creation.js +3 -1
  33. package/package.json +15 -15
  34. package/src/index.ts +83 -49
  35. package/src/oracle/interfaces.ts +6 -1
  36. package/src/oracle/txe_oracle_top_level_context.ts +111 -77
  37. package/src/rpc_translator.ts +57 -23
  38. package/src/state_machine/archiver.ts +2 -0
  39. package/src/state_machine/dummy_p2p_client.ts +40 -22
  40. package/src/state_machine/index.ts +25 -9
  41. package/src/state_machine/mock_epoch_cache.ts +5 -0
  42. package/src/txe_session.ts +89 -81
  43. package/src/util/txe_public_contract_data_source.ts +10 -36
  44. package/src/utils/block_creation.ts +3 -1
  45. package/dest/util/txe_contract_store.d.ts +0 -12
  46. package/dest/util/txe_contract_store.d.ts.map +0 -1
  47. package/dest/util/txe_contract_store.js +0 -22
  48. package/src/util/txe_contract_store.ts +0 -36
package/src/index.ts CHANGED
@@ -9,9 +9,12 @@ import { Fr } from '@aztec/aztec.js/fields';
9
9
  import { PublicKeys, deriveKeys } from '@aztec/aztec.js/keys';
10
10
  import { createSafeJsonRpcServer } from '@aztec/foundation/json-rpc/server';
11
11
  import type { Logger } from '@aztec/foundation/log';
12
- import { type ProtocolContract, protocolContractNames } from '@aztec/protocol-contracts';
12
+ import { openTmpStore } from '@aztec/kv-store/lmdb-v2';
13
+ import { protocolContractNames } from '@aztec/protocol-contracts';
13
14
  import { BundledProtocolContractsProvider } from '@aztec/protocol-contracts/providers/bundle';
15
+ import { ContractStore } from '@aztec/pxe/server';
14
16
  import { computeArtifactHash } from '@aztec/stdlib/contract';
17
+ import type { ContractArtifactWithHash } from '@aztec/stdlib/contract';
15
18
  import type { ApiSchemaFor } from '@aztec/stdlib/schemas';
16
19
  import { zodFor } from '@aztec/stdlib/schemas';
17
20
 
@@ -33,18 +36,24 @@ import {
33
36
  fromSingle,
34
37
  toSingle,
35
38
  } from './util/encoding.js';
36
- import type { ContractArtifactWithHash } from './util/txe_contract_store.js';
37
39
 
38
40
  const sessions = new Map<number, TXESession>();
39
41
 
40
42
  /*
41
43
  * TXE typically has to load the same contract artifacts over and over again for multiple tests,
42
- * so we cache them here to avoid both loading them from disk repeatedly and computing their artifact hashes
44
+ * so we cache them here to avoid loading from disk repeatedly.
45
+ *
46
+ * The in-flight map coalesces concurrent requests for the same cache key so that
47
+ * computeArtifactHash (very expensive) is only run once even under parallelism.
43
48
  */
44
49
  const TXEArtifactsCache = new Map<
45
50
  string,
46
51
  { artifact: ContractArtifactWithHash; instance: ContractInstanceWithAddress }
47
52
  >();
53
+ const TXEArtifactsCacheInFlight = new Map<
54
+ string,
55
+ Promise<{ artifact: ContractArtifactWithHash; instance: ContractInstanceWithAddress }>
56
+ >();
48
57
 
49
58
  type TXEForeignCallInput = {
50
59
  session_id: number;
@@ -68,7 +77,7 @@ const TXEForeignCallInputSchema = zodFor<TXEForeignCallInput>()(
68
77
  );
69
78
 
70
79
  class TXEDispatcher {
71
- private protocolContracts!: ProtocolContract[];
80
+ private contractStore!: ContractStore;
72
81
 
73
82
  constructor(private logger: Logger) {}
74
83
 
@@ -135,29 +144,36 @@ class TXEDispatcher {
135
144
  this.logger.debug(`Using cached artifact for ${cacheKey}`);
136
145
  ({ artifact, instance } = TXEArtifactsCache.get(cacheKey)!);
137
146
  } else {
138
- this.logger.debug(`Loading compiled artifact ${artifactPath}`);
139
- const artifactJSON = JSON.parse(await readFile(artifactPath, 'utf-8')) as NoirCompiledContract;
140
- const artifactWithoutHash = loadContractArtifact(artifactJSON);
141
- artifact = {
142
- ...artifactWithoutHash,
143
- // Artifact hash is *very* expensive to compute, so we do it here once
144
- // and the TXE contract data provider can cache it
145
- artifactHash: await computeArtifactHash(artifactWithoutHash),
146
- };
147
- this.logger.debug(
148
- `Deploy ${
149
- artifact.name
150
- } with initializer ${initializer}(${decodedArgs}) and public keys hash ${publicKeysHash.toString()}`,
151
- );
152
- instance = await getContractInstanceFromInstantiationParams(artifact, {
153
- constructorArgs: decodedArgs,
154
- skipArgsDecoding: true,
155
- salt: Fr.ONE,
156
- publicKeys,
157
- constructorArtifact: initializer ? initializer : undefined,
158
- deployer: AztecAddress.ZERO,
159
- });
160
- TXEArtifactsCache.set(cacheKey, { artifact, instance });
147
+ if (!TXEArtifactsCacheInFlight.has(cacheKey)) {
148
+ this.logger.debug(`Loading compiled artifact ${artifactPath}`);
149
+ const compute = async () => {
150
+ const artifactJSON = JSON.parse(await readFile(artifactPath, 'utf-8')) as NoirCompiledContract;
151
+ const artifactWithoutHash = loadContractArtifact(artifactJSON);
152
+ const computedArtifact: ContractArtifactWithHash = {
153
+ ...artifactWithoutHash,
154
+ // Artifact hash is *very* expensive to compute, so we do it here once
155
+ // and the TXE contract data provider can cache it
156
+ artifactHash: await computeArtifactHash(artifactWithoutHash),
157
+ };
158
+ this.logger.debug(
159
+ `Deploy ${computedArtifact.name} with initializer ${initializer}(${decodedArgs}) and public keys hash ${publicKeysHash.toString()}`,
160
+ );
161
+ const computedInstance = await getContractInstanceFromInstantiationParams(computedArtifact, {
162
+ constructorArgs: decodedArgs,
163
+ skipArgsDecoding: true,
164
+ salt: Fr.ONE,
165
+ publicKeys,
166
+ constructorArtifact: initializer ? initializer : undefined,
167
+ deployer: AztecAddress.ZERO,
168
+ });
169
+ const result = { artifact: computedArtifact, instance: computedInstance };
170
+ TXEArtifactsCache.set(cacheKey, result);
171
+ TXEArtifactsCacheInFlight.delete(cacheKey);
172
+ return result;
173
+ };
174
+ TXEArtifactsCacheInFlight.set(cacheKey, compute());
175
+ }
176
+ ({ artifact, instance } = await TXEArtifactsCacheInFlight.get(cacheKey)!);
161
177
  }
162
178
 
163
179
  inputs.splice(0, 1, artifact, instance, toSingle(secret));
@@ -175,23 +191,35 @@ class TXEDispatcher {
175
191
  this.logger.debug(`Using cached artifact for ${cacheKey}`);
176
192
  ({ artifact, instance } = TXEArtifactsCache.get(cacheKey)!);
177
193
  } else {
178
- const keys = await deriveKeys(secret);
179
- const args = [keys.publicKeys.masterIncomingViewingPublicKey.x, keys.publicKeys.masterIncomingViewingPublicKey.y];
180
- artifact = {
181
- ...SchnorrAccountContractArtifact,
182
- // Artifact hash is *very* expensive to compute, so we do it here once
183
- // and the TXE contract data provider can cache it
184
- artifactHash: await computeArtifactHash(SchnorrAccountContractArtifact),
185
- };
186
- instance = await getContractInstanceFromInstantiationParams(artifact, {
187
- constructorArgs: args,
188
- skipArgsDecoding: true,
189
- salt: Fr.ONE,
190
- publicKeys: keys.publicKeys,
191
- constructorArtifact: 'constructor',
192
- deployer: AztecAddress.ZERO,
193
- });
194
- TXEArtifactsCache.set(cacheKey, { artifact, instance });
194
+ if (!TXEArtifactsCacheInFlight.has(cacheKey)) {
195
+ const compute = async () => {
196
+ const keys = await deriveKeys(secret);
197
+ const args = [
198
+ keys.publicKeys.masterIncomingViewingPublicKey.x,
199
+ keys.publicKeys.masterIncomingViewingPublicKey.y,
200
+ ];
201
+ const computedArtifact: ContractArtifactWithHash = {
202
+ ...SchnorrAccountContractArtifact,
203
+ // Artifact hash is *very* expensive to compute, so we do it here once
204
+ // and the TXE contract data provider can cache it
205
+ artifactHash: await computeArtifactHash(SchnorrAccountContractArtifact),
206
+ };
207
+ const computedInstance = await getContractInstanceFromInstantiationParams(computedArtifact, {
208
+ constructorArgs: args,
209
+ skipArgsDecoding: true,
210
+ salt: Fr.ONE,
211
+ publicKeys: keys.publicKeys,
212
+ constructorArtifact: 'constructor',
213
+ deployer: AztecAddress.ZERO,
214
+ });
215
+ const result = { artifact: computedArtifact, instance: computedInstance };
216
+ TXEArtifactsCache.set(cacheKey, result);
217
+ TXEArtifactsCacheInFlight.delete(cacheKey);
218
+ return result;
219
+ };
220
+ TXEArtifactsCacheInFlight.set(cacheKey, compute());
221
+ }
222
+ ({ artifact, instance } = await TXEArtifactsCacheInFlight.get(cacheKey)!);
195
223
  }
196
224
 
197
225
  inputs.splice(0, 0, artifact, instance);
@@ -204,12 +232,18 @@ class TXEDispatcher {
204
232
 
205
233
  if (!sessions.has(sessionId)) {
206
234
  this.logger.debug(`Creating new session ${sessionId}`);
207
- if (!this.protocolContracts) {
208
- this.protocolContracts = await Promise.all(
209
- protocolContractNames.map(name => new BundledProtocolContractsProvider().getProtocolContractArtifact(name)),
210
- );
235
+ if (!this.contractStore) {
236
+ const kvStore = await openTmpStore('txe-contracts');
237
+ this.contractStore = new ContractStore(kvStore);
238
+ const provider = new BundledProtocolContractsProvider();
239
+ for (const name of protocolContractNames) {
240
+ const { instance, artifact } = await provider.getProtocolContractArtifact(name);
241
+ await this.contractStore.addContractArtifact(artifact);
242
+ await this.contractStore.addContractInstance(instance);
243
+ }
244
+ this.logger.debug('Registered protocol contracts in shared contract store');
211
245
  }
212
- sessions.set(sessionId, await TXESession.init(this.protocolContracts));
246
+ sessions.set(sessionId, await TXESession.init(this.contractStore));
213
247
  }
214
248
 
215
249
  switch (functionName) {
@@ -71,11 +71,13 @@ export interface ITxeExecutionOracle {
71
71
  args: Fr[],
72
72
  argsHash: Fr,
73
73
  isStaticCall: boolean,
74
+ jobId: string,
74
75
  ): Promise<Fr[]>;
75
- txeSimulateUtilityFunction(
76
+ txeExecuteUtilityFunction(
76
77
  targetContractAddress: AztecAddress,
77
78
  functionSelector: FunctionSelector,
78
79
  args: Fr[],
80
+ jobId: string,
79
81
  ): Promise<Fr[]>;
80
82
  txePublicCallNewFlow(
81
83
  from: AztecAddress,
@@ -83,4 +85,7 @@ export interface ITxeExecutionOracle {
83
85
  calldata: Fr[],
84
86
  isStaticCall: boolean,
85
87
  ): Promise<Fr[]>;
88
+ // TODO(F-335): Drop this from here as it's not a real oracle handler - it's only called from
89
+ // RPCTranslator::txeGetPrivateEvents and never from Noir.
90
+ syncContractNonOracleMethod(contractAddress: AztecAddress, scope: AztecAddress, jobId: string): Promise<void>;
86
91
  }
@@ -12,9 +12,11 @@ import { Fr } from '@aztec/foundation/curves/bn254';
12
12
  import { LogLevels, type Logger, applyStringFormatting, createLogger } from '@aztec/foundation/log';
13
13
  import { TestDateProvider } from '@aztec/foundation/timer';
14
14
  import type { KeyStore } from '@aztec/key-store';
15
+ import type { AccessScopes } from '@aztec/pxe/client/lazy';
15
16
  import {
16
17
  AddressStore,
17
18
  CapsuleStore,
19
+ type ContractStore,
18
20
  NoteStore,
19
21
  ORACLE_VERSION,
20
22
  PrivateEventStore,
@@ -22,7 +24,6 @@ import {
22
24
  SenderAddressBookStore,
23
25
  SenderTaggingStore,
24
26
  enrichPublicSimulationError,
25
- syncState,
26
27
  } from '@aztec/pxe/server';
27
28
  import {
28
29
  ExecutionNoteCache,
@@ -84,7 +85,6 @@ import { ForkCheckpoint } from '@aztec/world-state';
84
85
  import { DEFAULT_ADDRESS } from '../constants.js';
85
86
  import type { TXEStateMachine } from '../state_machine/index.js';
86
87
  import type { TXEAccountStore } from '../util/txe_account_store.js';
87
- import type { TXEContractStore } from '../util/txe_contract_store.js';
88
88
  import { TXEPublicContractDataSource } from '../util/txe_public_contract_data_source.js';
89
89
  import { getSingleTxBlockRequestHash, insertTxEffectIntoWorldTrees, makeTXEBlock } from '../utils/block_creation.js';
90
90
  import type { ITxeExecutionOracle } from './interfaces.js';
@@ -97,7 +97,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
97
97
 
98
98
  constructor(
99
99
  private stateMachine: TXEStateMachine,
100
- private contractStore: TXEContractStore,
100
+ private contractStore: ContractStore,
101
101
  private noteStore: NoteStore,
102
102
  private keyStore: KeyStore,
103
103
  private addressStore: AddressStore,
@@ -107,7 +107,6 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
107
107
  private senderAddressBookStore: SenderAddressBookStore,
108
108
  private capsuleStore: CapsuleStore,
109
109
  private privateEventStore: PrivateEventStore,
110
- private jobId: string,
111
110
  private nextBlockTimestamp: bigint,
112
111
  private version: Fr,
113
112
  private chainId: Fr,
@@ -132,13 +131,14 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
132
131
  }
133
132
 
134
133
  // We instruct users to debug contracts via this oracle, so it makes sense that they'd expect it to also work in tests
135
- utilityDebugLog(level: number, message: string, fields: Fr[]): void {
134
+ utilityLog(level: number, message: string, fields: Fr[]): Promise<void> {
136
135
  if (!LogLevels[level]) {
137
- throw new Error(`Invalid debug log level: ${level}`);
136
+ throw new Error(`Invalid log level: ${level}`);
138
137
  }
139
138
  const levelName = LogLevels[level];
140
139
 
141
140
  this.logger[levelName](`${applyStringFormatting(message, fields)}`, { module: `${this.logger.module}:debug_log` });
141
+ return Promise.resolve();
142
142
  }
143
143
 
144
144
  txeGetDefaultAddress(): AztecAddress {
@@ -171,6 +171,25 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
171
171
  return { txHash: txEffects.txHash, noteHashes: txEffects.noteHashes, nullifiers: txEffects.nullifiers };
172
172
  }
173
173
 
174
+ async syncContractNonOracleMethod(contractAddress: AztecAddress, scope: AztecAddress, jobId: string) {
175
+ if (contractAddress.equals(DEFAULT_ADDRESS)) {
176
+ this.logger.debug(`Skipping sync in txeGetPrivateEvents because the events correspond to the default address.`);
177
+ return;
178
+ }
179
+
180
+ const blockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
181
+ await this.stateMachine.contractSyncService.ensureContractSynced(
182
+ contractAddress,
183
+ null,
184
+ async (call, execScopes) => {
185
+ await this.executeUtilityCall(call, execScopes, jobId);
186
+ },
187
+ blockHeader,
188
+ jobId,
189
+ [scope],
190
+ );
191
+ }
192
+
174
193
  async txeGetPrivateEvents(selector: EventSelector, contractAddress: AztecAddress, scope: AztecAddress) {
175
194
  return (
176
195
  await this.privateEventStore.getPrivateEvents(selector, {
@@ -210,7 +229,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
210
229
  await this.txeAddAccount(artifact, instance, secret);
211
230
  } else {
212
231
  await this.contractStore.addContractInstance(instance);
213
- await this.contractStore.addContractArtifact(instance.currentContractClassId, artifact);
232
+ await this.contractStore.addContractArtifact(artifact);
214
233
  this.logger.debug(`Deployed ${artifact.name} at ${instance.address}`);
215
234
  }
216
235
  }
@@ -220,7 +239,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
220
239
 
221
240
  this.logger.debug(`Deployed ${artifact.name} at ${instance.address}`);
222
241
  await this.contractStore.addContractInstance(instance);
223
- await this.contractStore.addContractArtifact(instance.currentContractClassId, artifact);
242
+ await this.contractStore.addContractArtifact(artifact);
224
243
 
225
244
  const completeAddress = await this.keyStore.addAccount(secret, partialAddress);
226
245
  await this.accountStore.setAccount(completeAddress.address, completeAddress);
@@ -284,6 +303,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
284
303
  args: Fr[],
285
304
  argsHash: Fr = Fr.zero(),
286
305
  isStaticCall: boolean = false,
306
+ jobId: string,
287
307
  ) {
288
308
  this.logger.verbose(
289
309
  `Executing external function ${await this.contractStore.getDebugFunctionName(targetContractAddress, functionSelector)}@${targetContractAddress} isStaticCall=${isStaticCall}`,
@@ -297,12 +317,24 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
297
317
  throw new Error(message);
298
318
  }
299
319
 
320
+ // When `from` is the zero address (e.g. when deploying a new account contract), we return an
321
+ // empty scope list which acts as deny-all: no notes are visible and no keys are accessible.
322
+ const effectiveScopes = from.isZero() ? [] : [from];
323
+
300
324
  // Sync notes before executing private function to discover notes from previous transactions
301
- const utilityExecutor = async (call: FunctionCall) => {
302
- await this.executeUtilityCall(call);
325
+ const utilityExecutor = async (call: FunctionCall, execScopes: AccessScopes) => {
326
+ await this.executeUtilityCall(call, execScopes, jobId);
303
327
  };
304
328
 
305
- await syncState(targetContractAddress, this.contractStore, functionSelector, utilityExecutor);
329
+ const blockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
330
+ await this.stateMachine.contractSyncService.ensureContractSynced(
331
+ targetContractAddress,
332
+ functionSelector,
333
+ utilityExecutor,
334
+ blockHeader,
335
+ jobId,
336
+ effectiveScopes,
337
+ );
306
338
 
307
339
  const blockNumber = await this.txeGetNextBlockNumber();
308
340
 
@@ -314,8 +346,6 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
314
346
 
315
347
  const txContext = new TxContext(this.chainId, this.version, gasSettings);
316
348
 
317
- const blockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
318
-
319
349
  const protocolNullifier = await computeProtocolNullifier(getSingleTxBlockRequestHash(blockNumber));
320
350
  const noteCache = new ExecutionNoteCache(protocolNullifier);
321
351
  // In production, the account contract sets the min revertible counter before calling the app function.
@@ -327,43 +357,37 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
327
357
 
328
358
  const simulator = new WASMSimulator();
329
359
 
330
- const privateExecutionOracle = new PrivateExecutionOracle(
360
+ const privateExecutionOracle = new PrivateExecutionOracle({
331
361
  argsHash,
332
362
  txContext,
333
363
  callContext,
334
- /** Header of a block whose state is used during private execution (not the block the transaction is included in). */
335
- blockHeader,
364
+ anchorBlockHeader: blockHeader,
336
365
  utilityExecutor,
337
- /** List of transient auth witnesses to be used during this simulation */
338
- Array.from(this.authwits.values()),
339
- /** List of transient auth witnesses to be used during this simulation */
340
- [],
341
- HashedValuesCache.create([new HashedValues(args, argsHash)]),
366
+ authWitnesses: Array.from(this.authwits.values()),
367
+ capsules: [],
368
+ executionCache: HashedValuesCache.create([new HashedValues(args, argsHash)]),
342
369
  noteCache,
343
370
  taggingIndexCache,
344
- this.contractStore,
345
- this.noteStore,
346
- this.keyStore,
347
- this.addressStore,
348
- this.stateMachine.node,
349
- this.stateMachine.anchorBlockStore,
350
- this.senderTaggingStore,
351
- this.recipientTaggingStore,
352
- this.senderAddressBookStore,
353
- this.capsuleStore,
354
- this.privateEventStore,
355
- this.jobId,
356
- 0, // totalPublicArgsCount
357
- minRevertibleSideEffectCounter, // (start) sideEffectCounter
358
- undefined, // log
359
- undefined, // scopes
360
- /**
361
- * In TXE, the typical transaction entrypoint is skipped, so we need to simulate the actions that such a
362
- * contract would perform, including setting senderForTags.
363
- */
364
- from,
371
+ contractStore: this.contractStore,
372
+ noteStore: this.noteStore,
373
+ keyStore: this.keyStore,
374
+ addressStore: this.addressStore,
375
+ aztecNode: this.stateMachine.node,
376
+ senderTaggingStore: this.senderTaggingStore,
377
+ recipientTaggingStore: this.recipientTaggingStore,
378
+ senderAddressBookStore: this.senderAddressBookStore,
379
+ capsuleStore: this.capsuleStore,
380
+ privateEventStore: this.privateEventStore,
381
+ contractSyncService: this.stateMachine.contractSyncService,
382
+ jobId,
383
+ totalPublicCalldataCount: 0,
384
+ sideEffectCounter: minRevertibleSideEffectCounter,
385
+ scopes: effectiveScopes,
386
+ // In TXE, the typical transaction entrypoint is skipped, so we need to simulate the actions that such a
387
+ // contract would perform, including setting senderForTags.
388
+ senderForTags: from,
365
389
  simulator,
366
- );
390
+ });
367
391
 
368
392
  // Note: This is a slight modification of simulator.run without any of the checks. Maybe we should modify simulator.run with a boolean value to skip checks.
369
393
  let result: PrivateExecutionResult;
@@ -402,7 +426,8 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
402
426
  // We pass the non-zero minRevertibleSideEffectCounter to make sure the side effects are split correctly.
403
427
  const { publicInputs } = await generateSimulatedProvingResult(
404
428
  result,
405
- this.contractStore,
429
+ (addr, sel) => this.contractStore.getDebugFunctionName(addr, sel),
430
+ this.stateMachine.node,
406
431
  minRevertibleSideEffectCounter,
407
432
  );
408
433
 
@@ -585,7 +610,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
585
610
  constantData,
586
611
  /*gasUsed=*/ new Gas(0, 0),
587
612
  /*feePayer=*/ AztecAddress.zero(),
588
- /*includeByTimestamp=*/ 0n,
613
+ /*expirationTimestamp=*/ 0n,
589
614
  inputsForPublic,
590
615
  undefined,
591
616
  );
@@ -653,10 +678,11 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
653
678
  return returnValues ?? [];
654
679
  }
655
680
 
656
- async txeSimulateUtilityFunction(
681
+ async txeExecuteUtilityFunction(
657
682
  targetContractAddress: AztecAddress,
658
683
  functionSelector: FunctionSelector,
659
684
  args: Fr[],
685
+ jobId: string,
660
686
  ) {
661
687
  const artifact = await this.contractStore.getFunctionArtifact(targetContractAddress, functionSelector);
662
688
  if (!artifact) {
@@ -664,25 +690,33 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
664
690
  }
665
691
 
666
692
  // Sync notes before executing utility function to discover notes from previous transactions
667
- await syncState(targetContractAddress, this.contractStore, functionSelector, async call => {
668
- await this.executeUtilityCall(call);
669
- });
670
-
671
- const call = new FunctionCall(
672
- artifact.name,
693
+ const blockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
694
+ await this.stateMachine.contractSyncService.ensureContractSynced(
673
695
  targetContractAddress,
674
696
  functionSelector,
675
- FunctionType.UTILITY,
676
- false,
677
- false,
678
- args,
679
- [],
697
+ async (call, execScopes) => {
698
+ await this.executeUtilityCall(call, execScopes, jobId);
699
+ },
700
+ blockHeader,
701
+ jobId,
702
+ 'ALL_SCOPES',
680
703
  );
681
704
 
682
- return this.executeUtilityCall(call);
705
+ const call = FunctionCall.from({
706
+ name: artifact.name,
707
+ to: targetContractAddress,
708
+ selector: functionSelector,
709
+ type: FunctionType.UTILITY,
710
+ hideMsgSender: false,
711
+ isStatic: false,
712
+ args,
713
+ returnTypes: [],
714
+ });
715
+
716
+ return this.executeUtilityCall(call, 'ALL_SCOPES', jobId);
683
717
  }
684
718
 
685
- private async executeUtilityCall(call: FunctionCall): Promise<Fr[]> {
719
+ private async executeUtilityCall(call: FunctionCall, scopes: AccessScopes, jobId: string): Promise<Fr[]> {
686
720
  const entryPointArtifact = await this.contractStore.getFunctionArtifactWithDebugMetadata(call.to, call.selector);
687
721
  if (entryPointArtifact.functionType !== FunctionType.UTILITY) {
688
722
  throw new Error(`Cannot run ${entryPointArtifact.functionType} function as utility`);
@@ -695,23 +729,23 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
695
729
 
696
730
  try {
697
731
  const anchorBlockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
698
- const oracle = new UtilityExecutionOracle(
699
- call.to,
700
- [],
701
- [],
732
+ const oracle = new UtilityExecutionOracle({
733
+ contractAddress: call.to,
734
+ authWitnesses: [],
735
+ capsules: [],
702
736
  anchorBlockHeader,
703
- this.contractStore,
704
- this.noteStore,
705
- this.keyStore,
706
- this.addressStore,
707
- this.stateMachine.node,
708
- this.stateMachine.anchorBlockStore,
709
- this.recipientTaggingStore,
710
- this.senderAddressBookStore,
711
- this.capsuleStore,
712
- this.privateEventStore,
713
- this.jobId,
714
- );
737
+ contractStore: this.contractStore,
738
+ noteStore: this.noteStore,
739
+ keyStore: this.keyStore,
740
+ addressStore: this.addressStore,
741
+ aztecNode: this.stateMachine.node,
742
+ recipientTaggingStore: this.recipientTaggingStore,
743
+ senderAddressBookStore: this.senderAddressBookStore,
744
+ capsuleStore: this.capsuleStore,
745
+ privateEventStore: this.privateEventStore,
746
+ jobId,
747
+ scopes,
748
+ });
715
749
  const acirExecutionResult = await new WASMSimulator()
716
750
  .executeUserCircuit(toACVMWitness(0, call.args), entryPointArtifact, new Oracle(oracle).toACIRCallback())
717
751
  .catch((err: Error) => {
@@ -727,10 +761,10 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
727
761
  );
728
762
  });
729
763
 
730
- this.logger.verbose(`Utility simulation for ${call.to}.${call.selector} completed`);
764
+ this.logger.verbose(`Utility execution for ${call.to}.${call.selector} completed`);
731
765
  return witnessMapToFields(acirExecutionResult.returnWitness);
732
766
  } catch (err) {
733
- throw createSimulationError(err instanceof Error ? err : new Error('Unknown error during utility simulation'));
767
+ throw createSimulationError(err instanceof Error ? err : new Error('Unknown error during utility execution'));
734
768
  }
735
769
  }
736
770