@aztec/txe 0.0.1-commit.fffb133c → 0.0.1-dev

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 (62) hide show
  1. package/dest/index.d.ts +1 -1
  2. package/dest/index.d.ts.map +1 -1
  3. package/dest/index.js +88 -54
  4. package/dest/oracle/interfaces.d.ts +35 -28
  5. package/dest/oracle/interfaces.d.ts.map +1 -1
  6. package/dest/oracle/txe_oracle_public_context.d.ts +13 -13
  7. package/dest/oracle/txe_oracle_public_context.d.ts.map +1 -1
  8. package/dest/oracle/txe_oracle_public_context.js +14 -15
  9. package/dest/oracle/txe_oracle_top_level_context.d.ts +33 -23
  10. package/dest/oracle/txe_oracle_top_level_context.d.ts.map +1 -1
  11. package/dest/oracle/txe_oracle_top_level_context.js +185 -71
  12. package/dest/rpc_translator.d.ts +135 -83
  13. package/dest/rpc_translator.d.ts.map +1 -1
  14. package/dest/rpc_translator.js +540 -178
  15. package/dest/state_machine/archiver.d.ts +3 -3
  16. package/dest/state_machine/archiver.d.ts.map +1 -1
  17. package/dest/state_machine/archiver.js +7 -7
  18. package/dest/state_machine/dummy_p2p_client.d.ts +16 -12
  19. package/dest/state_machine/dummy_p2p_client.d.ts.map +1 -1
  20. package/dest/state_machine/dummy_p2p_client.js +28 -16
  21. package/dest/state_machine/index.d.ts +7 -5
  22. package/dest/state_machine/index.d.ts.map +1 -1
  23. package/dest/state_machine/index.js +19 -10
  24. package/dest/state_machine/mock_epoch_cache.d.ts +3 -1
  25. package/dest/state_machine/mock_epoch_cache.d.ts.map +1 -1
  26. package/dest/state_machine/mock_epoch_cache.js +4 -0
  27. package/dest/state_machine/synchronizer.d.ts +5 -5
  28. package/dest/state_machine/synchronizer.d.ts.map +1 -1
  29. package/dest/state_machine/synchronizer.js +3 -3
  30. package/dest/txe_oracle_version.d.ts +10 -0
  31. package/dest/txe_oracle_version.d.ts.map +1 -0
  32. package/dest/txe_oracle_version.js +8 -0
  33. package/dest/txe_session.d.ts +67 -8
  34. package/dest/txe_session.d.ts.map +1 -1
  35. package/dest/txe_session.js +175 -32
  36. package/dest/util/encoding.d.ts +43 -41
  37. package/dest/util/encoding.d.ts.map +1 -1
  38. package/dest/util/txe_public_contract_data_source.d.ts +2 -3
  39. package/dest/util/txe_public_contract_data_source.d.ts.map +1 -1
  40. package/dest/util/txe_public_contract_data_source.js +5 -22
  41. package/dest/utils/block_creation.d.ts +1 -1
  42. package/dest/utils/block_creation.d.ts.map +1 -1
  43. package/dest/utils/block_creation.js +3 -1
  44. package/package.json +15 -15
  45. package/src/index.ts +89 -52
  46. package/src/oracle/interfaces.ts +43 -34
  47. package/src/oracle/txe_oracle_public_context.ts +14 -16
  48. package/src/oracle/txe_oracle_top_level_context.ts +231 -132
  49. package/src/rpc_translator.ts +673 -208
  50. package/src/state_machine/archiver.ts +7 -5
  51. package/src/state_machine/dummy_p2p_client.ts +40 -22
  52. package/src/state_machine/index.ts +30 -10
  53. package/src/state_machine/mock_epoch_cache.ts +5 -0
  54. package/src/state_machine/synchronizer.ts +4 -4
  55. package/src/txe_oracle_version.ts +9 -0
  56. package/src/txe_session.ts +270 -93
  57. package/src/util/txe_public_contract_data_source.ts +10 -36
  58. package/src/utils/block_creation.ts +3 -1
  59. package/dest/util/txe_contract_store.d.ts +0 -12
  60. package/dest/util/txe_contract_store.d.ts.map +0 -1
  61. package/dest/util/txe_contract_store.js +0 -22
  62. package/src/util/txe_contract_store.ts +0 -36
@@ -1,11 +1,4 @@
1
- import {
2
- CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS,
3
- DEFAULT_DA_GAS_LIMIT,
4
- DEFAULT_L2_GAS_LIMIT,
5
- DEFAULT_TEARDOWN_DA_GAS_LIMIT,
6
- DEFAULT_TEARDOWN_L2_GAS_LIMIT,
7
- NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP,
8
- } from '@aztec/constants';
1
+ import { CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP } from '@aztec/constants';
9
2
  import { BlockNumber } from '@aztec/foundation/branded-types';
10
3
  import { Schnorr } from '@aztec/foundation/crypto/schnorr';
11
4
  import { Fr } from '@aztec/foundation/curves/bn254';
@@ -14,15 +7,18 @@ import { TestDateProvider } from '@aztec/foundation/timer';
14
7
  import type { KeyStore } from '@aztec/key-store';
15
8
  import {
16
9
  AddressStore,
10
+ CapsuleService,
17
11
  CapsuleStore,
12
+ type ContractStore,
13
+ type ExecutionHooks,
18
14
  NoteStore,
19
- ORACLE_VERSION,
15
+ ORACLE_VERSION_MAJOR,
20
16
  PrivateEventStore,
21
17
  RecipientTaggingStore,
22
18
  SenderAddressBookStore,
23
19
  SenderTaggingStore,
20
+ composeHooks,
24
21
  enrichPublicSimulationError,
25
- syncState,
26
22
  } from '@aztec/pxe/server';
27
23
  import {
28
24
  ExecutionNoteCache,
@@ -84,7 +80,6 @@ import { ForkCheckpoint } from '@aztec/world-state';
84
80
  import { DEFAULT_ADDRESS } from '../constants.js';
85
81
  import type { TXEStateMachine } from '../state_machine/index.js';
86
82
  import type { TXEAccountStore } from '../util/txe_account_store.js';
87
- import type { TXEContractStore } from '../util/txe_contract_store.js';
88
83
  import { TXEPublicContractDataSource } from '../util/txe_public_contract_data_source.js';
89
84
  import { getSingleTxBlockRequestHash, insertTxEffectIntoWorldTrees, makeTXEBlock } from '../utils/block_creation.js';
90
85
  import type { ITxeExecutionOracle } from './interfaces.js';
@@ -97,7 +92,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
97
92
 
98
93
  constructor(
99
94
  private stateMachine: TXEStateMachine,
100
- private contractStore: TXEContractStore,
95
+ private contractStore: ContractStore,
101
96
  private noteStore: NoteStore,
102
97
  private keyStore: KeyStore,
103
98
  private addressStore: AddressStore,
@@ -107,7 +102,6 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
107
102
  private senderAddressBookStore: SenderAddressBookStore,
108
103
  private capsuleStore: CapsuleStore,
109
104
  private privateEventStore: PrivateEventStore,
110
- private jobId: string,
111
105
  private nextBlockTimestamp: bigint,
112
106
  private version: Fr,
113
107
  private chainId: Fr,
@@ -117,47 +111,60 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
117
111
  this.logger.debug('Entering Top Level Context');
118
112
  }
119
113
 
120
- utilityAssertCompatibleOracleVersion(version: number): void {
121
- if (version !== ORACLE_VERSION) {
114
+ private contractOracleVersion: { major: number; minor: number } | undefined;
115
+
116
+ assertCompatibleOracleVersion(major: number, minor: number): void {
117
+ if (major !== ORACLE_VERSION_MAJOR) {
118
+ const hint =
119
+ major > ORACLE_VERSION_MAJOR
120
+ ? 'The contract was compiled with a newer version of Aztec.nr than this aztec cli version supports. Upgrade your aztec cli version to a compatible version.'
121
+ : 'The contract was compiled with an older version of Aztec.nr than this aztec cli version supports. Recompile the contract with a compatible version of Aztec.nr.';
122
122
  throw new Error(
123
- `Incompatible oracle version. TXE is using version '${ORACLE_VERSION}', but got a request for '${version}'.`,
123
+ `Incompatible aztec cli version: ${hint} See https://docs.aztec.network/errors/8 (expected oracle major version ${ORACLE_VERSION_MAJOR}, got ${major})`,
124
124
  );
125
125
  }
126
+ this.contractOracleVersion = { major, minor };
127
+ }
128
+
129
+ // Prefixed with "nonOracleFunction" as it is not used as an oracle handler.
130
+ nonOracleFunctionGetContractOracleVersion(): { major: number; minor: number } | undefined {
131
+ return this.contractOracleVersion;
126
132
  }
127
133
 
128
134
  // This is typically only invoked in private contexts, but it is convenient to also have it in top-level for testing
129
135
  // setup.
130
- utilityGetRandomField(): Fr {
136
+ getRandomField(): Fr {
131
137
  return Fr.random();
132
138
  }
133
139
 
134
140
  // 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 {
141
+ log(level: number, message: string, fields: Fr[]): Promise<void> {
136
142
  if (!LogLevels[level]) {
137
- throw new Error(`Invalid debug log level: ${level}`);
143
+ throw new Error(`Invalid log level: ${level}`);
138
144
  }
139
145
  const levelName = LogLevels[level];
140
146
 
141
147
  this.logger[levelName](`${applyStringFormatting(message, fields)}`, { module: `${this.logger.module}:debug_log` });
148
+ return Promise.resolve();
142
149
  }
143
150
 
144
- txeGetDefaultAddress(): AztecAddress {
151
+ getDefaultAddress(): AztecAddress {
145
152
  return DEFAULT_ADDRESS;
146
153
  }
147
154
 
148
- async txeGetNextBlockNumber(): Promise<BlockNumber> {
155
+ async getNextBlockNumber(): Promise<BlockNumber> {
149
156
  return BlockNumber((await this.getLastBlockNumber()) + 1);
150
157
  }
151
158
 
152
- txeGetNextBlockTimestamp(): Promise<bigint> {
159
+ getNextBlockTimestamp(): Promise<bigint> {
153
160
  return Promise.resolve(this.nextBlockTimestamp);
154
161
  }
155
162
 
156
- async txeGetLastBlockTimestamp() {
163
+ async getLastBlockTimestamp() {
157
164
  return (await this.stateMachine.node.getBlockHeader('latest'))!.globalVariables.timestamp;
158
165
  }
159
166
 
160
- async txeGetLastTxEffects() {
167
+ async getLastTxEffects() {
161
168
  const latestBlockNumber = await this.stateMachine.archiver.getBlockNumber();
162
169
  const block = await this.stateMachine.archiver.getBlock(latestBlockNumber);
163
170
 
@@ -168,10 +175,34 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
168
175
 
169
176
  const txEffects = block!.body.txEffects[0];
170
177
 
171
- return { txHash: txEffects.txHash, noteHashes: txEffects.noteHashes, nullifiers: txEffects.nullifiers };
178
+ return {
179
+ txHash: txEffects.txHash,
180
+ noteHashes: txEffects.noteHashes,
181
+ nullifiers: txEffects.nullifiers,
182
+ privateLogs: txEffects.privateLogs,
183
+ };
184
+ }
185
+
186
+ async syncContractNonOracleMethod(contractAddress: AztecAddress, scope: AztecAddress, jobId: string) {
187
+ if (contractAddress.equals(DEFAULT_ADDRESS)) {
188
+ this.logger.debug(`Skipping sync in getPrivateEvents because the events correspond to the default address.`);
189
+ return;
190
+ }
191
+
192
+ const blockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
193
+ await this.stateMachine.contractSyncService.ensureContractSynced(
194
+ contractAddress,
195
+ null,
196
+ async (call, execScopes) => {
197
+ await this.executeUtilityCall(call, execScopes, jobId);
198
+ },
199
+ blockHeader,
200
+ jobId,
201
+ [scope],
202
+ );
172
203
  }
173
204
 
174
- async txeGetPrivateEvents(selector: EventSelector, contractAddress: AztecAddress, scope: AztecAddress) {
205
+ async getPrivateEvents(selector: EventSelector, contractAddress: AztecAddress, scope: AztecAddress) {
175
206
  return (
176
207
  await this.privateEventStore.getPrivateEvents(selector, {
177
208
  contractAddress,
@@ -182,7 +213,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
182
213
  ).map(e => e.packedEvent);
183
214
  }
184
215
 
185
- async txeAdvanceBlocksBy(blocks: number) {
216
+ async advanceBlocksBy(blocks: number) {
186
217
  this.logger.debug(`time traveling ${blocks} blocks`);
187
218
 
188
219
  for (let i = 0; i < blocks; i++) {
@@ -190,12 +221,12 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
190
221
  }
191
222
  }
192
223
 
193
- txeAdvanceTimestampBy(duration: UInt64) {
224
+ advanceTimestampBy(duration: UInt64) {
194
225
  this.logger.debug(`time traveling ${duration} seconds`);
195
226
  this.nextBlockTimestamp += duration;
196
227
  }
197
228
 
198
- async txeDeploy(artifact: ContractArtifact, instance: ContractInstanceWithAddress, secret: Fr) {
229
+ async deploy(artifact: ContractArtifact, instance: ContractInstanceWithAddress, secret: Fr) {
199
230
  // Emit deployment nullifier
200
231
  await this.mineBlock({
201
232
  nullifiers: [
@@ -207,20 +238,20 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
207
238
  });
208
239
 
209
240
  if (!secret.equals(Fr.ZERO)) {
210
- await this.txeAddAccount(artifact, instance, secret);
241
+ await this.addAccount(artifact, instance, secret);
211
242
  } else {
212
243
  await this.contractStore.addContractInstance(instance);
213
- await this.contractStore.addContractArtifact(instance.currentContractClassId, artifact);
244
+ await this.contractStore.addContractArtifact(artifact);
214
245
  this.logger.debug(`Deployed ${artifact.name} at ${instance.address}`);
215
246
  }
216
247
  }
217
248
 
218
- async txeAddAccount(artifact: ContractArtifact, instance: ContractInstanceWithAddress, secret: Fr) {
249
+ async addAccount(artifact: ContractArtifact, instance: ContractInstanceWithAddress, secret: Fr) {
219
250
  const partialAddress = await computePartialAddress(instance);
220
251
 
221
252
  this.logger.debug(`Deployed ${artifact.name} at ${instance.address}`);
222
253
  await this.contractStore.addContractInstance(instance);
223
- await this.contractStore.addContractArtifact(instance.currentContractClassId, artifact);
254
+ await this.contractStore.addContractArtifact(artifact);
224
255
 
225
256
  const completeAddress = await this.keyStore.addAccount(secret, partialAddress);
226
257
  await this.accountStore.setAccount(completeAddress.address, completeAddress);
@@ -230,7 +261,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
230
261
  return completeAddress;
231
262
  }
232
263
 
233
- async txeCreateAccount(secret: Fr) {
264
+ async createAccount(secret: Fr) {
234
265
  // This is a foot gun !
235
266
  const completeAddress = await this.keyStore.addAccount(secret, secret);
236
267
  await this.accountStore.setAccount(completeAddress.address, completeAddress);
@@ -240,7 +271,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
240
271
  return completeAddress;
241
272
  }
242
273
 
243
- async txeAddAuthWitness(address: AztecAddress, messageHash: Fr) {
274
+ async addAuthWitness(address: AztecAddress, messageHash: Fr) {
244
275
  const account = await this.accountStore.getAccount(address);
245
276
  const privateKey = await this.keyStore.getMasterSecretKey(account.publicKeys.masterIncomingViewingPublicKey);
246
277
 
@@ -253,7 +284,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
253
284
  }
254
285
 
255
286
  async mineBlock(options: { nullifiers?: Fr[] } = {}) {
256
- const blockNumber = await this.txeGetNextBlockNumber();
287
+ const blockNumber = await this.getNextBlockNumber();
257
288
 
258
289
  const txEffect = TxEffect.empty();
259
290
  txEffect.nullifiers = [getSingleTxBlockRequestHash(blockNumber), ...(options.nullifiers ?? [])];
@@ -277,13 +308,17 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
277
308
  await this.stateMachine.handleL2Block(block);
278
309
  }
279
310
 
280
- async txePrivateCallNewFlow(
281
- from: AztecAddress,
311
+ async privateCallNewFlow(
312
+ from: AztecAddress | undefined,
282
313
  targetContractAddress: AztecAddress = AztecAddress.zero(),
283
314
  functionSelector: FunctionSelector = FunctionSelector.empty(),
284
315
  args: Fr[],
285
316
  argsHash: Fr = Fr.zero(),
286
317
  isStaticCall: boolean = false,
318
+ additionalScopes: AztecAddress[] = [],
319
+ jobId: string,
320
+ authorizedUtilityCallTargets: AztecAddress[],
321
+ gasSettings: GasSettings,
287
322
  ) {
288
323
  this.logger.verbose(
289
324
  `Executing external function ${await this.contractStore.getDebugFunctionName(targetContractAddress, functionSelector)}@${targetContractAddress} isStaticCall=${isStaticCall}`,
@@ -297,25 +332,30 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
297
332
  throw new Error(message);
298
333
  }
299
334
 
335
+ const scopes = from === undefined ? additionalScopes : [from, ...additionalScopes];
336
+
300
337
  // Sync notes before executing private function to discover notes from previous transactions
301
- const utilityExecutor = async (call: FunctionCall) => {
302
- await this.executeUtilityCall(call);
338
+ const utilityExecutor = async (call: FunctionCall, execScopes: AztecAddress[]) => {
339
+ await this.executeUtilityCall(call, execScopes, jobId);
303
340
  };
304
341
 
305
- await syncState(targetContractAddress, this.contractStore, functionSelector, utilityExecutor);
306
-
307
- const blockNumber = await this.txeGetNextBlockNumber();
342
+ const blockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
343
+ await this.stateMachine.contractSyncService.ensureContractSynced(
344
+ targetContractAddress,
345
+ functionSelector,
346
+ utilityExecutor,
347
+ blockHeader,
348
+ jobId,
349
+ scopes,
350
+ );
308
351
 
309
- const callContext = new CallContext(from, targetContractAddress, functionSelector, isStaticCall);
352
+ const blockNumber = await this.getNextBlockNumber();
310
353
 
311
- const gasLimits = new Gas(DEFAULT_DA_GAS_LIMIT, DEFAULT_L2_GAS_LIMIT);
312
- const teardownGasLimits = new Gas(DEFAULT_TEARDOWN_DA_GAS_LIMIT, DEFAULT_TEARDOWN_L2_GAS_LIMIT);
313
- const gasSettings = new GasSettings(gasLimits, teardownGasLimits, GasFees.empty(), GasFees.empty());
354
+ const msgSender = from ?? AztecAddress.NULL_MSG_SENDER;
355
+ const callContext = new CallContext(msgSender, targetContractAddress, functionSelector, isStaticCall);
314
356
 
315
357
  const txContext = new TxContext(this.chainId, this.version, gasSettings);
316
358
 
317
- const blockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
318
-
319
359
  const protocolNullifier = await computeProtocolNullifier(getSingleTxBlockRequestHash(blockNumber));
320
360
  const noteCache = new ExecutionNoteCache(protocolNullifier);
321
361
  // In production, the account contract sets the min revertible counter before calling the app function.
@@ -327,43 +367,42 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
327
367
 
328
368
  const simulator = new WASMSimulator();
329
369
 
330
- const privateExecutionOracle = new PrivateExecutionOracle(
370
+ const privateExecutionOracle = new PrivateExecutionOracle({
331
371
  argsHash,
332
372
  txContext,
333
373
  callContext,
334
- /** Header of a block whose state is used during private execution (not the block the transaction is included in). */
335
- blockHeader,
374
+ anchorBlockHeader: blockHeader,
336
375
  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)]),
376
+ authWitnesses: Array.from(this.authwits.values()),
377
+ capsules: [],
378
+ executionCache: HashedValuesCache.create([new HashedValues(args, argsHash)]),
342
379
  noteCache,
343
380
  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,
381
+ contractStore: this.contractStore,
382
+ noteStore: this.noteStore,
383
+ keyStore: this.keyStore,
384
+ addressStore: this.addressStore,
385
+ aztecNode: this.stateMachine.node,
386
+ senderTaggingStore: this.senderTaggingStore,
387
+ recipientTaggingStore: this.recipientTaggingStore,
388
+ senderAddressBookStore: this.senderAddressBookStore,
389
+ capsuleService: new CapsuleService(this.capsuleStore, scopes),
390
+ privateEventStore: this.privateEventStore,
391
+ contractSyncService: this.stateMachine.contractSyncService,
392
+ jobId,
393
+ totalPublicCalldataCount: 0,
394
+ sideEffectCounter: minRevertibleSideEffectCounter,
395
+ scopes,
396
+ // In TXE, the typical transaction entrypoint is skipped, so we need to simulate the actions that such a
397
+ // contract would perform, including setting senderForTags.
398
+ senderForTags: from,
365
399
  simulator,
366
- );
400
+ messageContextService: this.stateMachine.messageContextService,
401
+ l2TipsStore: this.stateMachine.node,
402
+ hooks: composeHooks({
403
+ authorizeUtilityCall: this.buildAuthorizeUtilityCallHook('private', authorizedUtilityCallTargets),
404
+ }),
405
+ });
367
406
 
368
407
  // 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
408
  let result: PrivateExecutionResult;
@@ -385,7 +424,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
385
424
  );
386
425
  const publicFunctionsCalldata = await Promise.all(
387
426
  publicCallRequests.map(async r => {
388
- const calldata = await privateExecutionOracle.privateLoadFromExecutionCache(r.calldataHash);
427
+ const calldata = await privateExecutionOracle.getHashPreimage(r.calldataHash);
389
428
  return new HashedValues(calldata, r.calldataHash);
390
429
  }),
391
430
  );
@@ -402,7 +441,8 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
402
441
  // We pass the non-zero minRevertibleSideEffectCounter to make sure the side effects are split correctly.
403
442
  const { publicInputs } = await generateSimulatedProvingResult(
404
443
  result,
405
- this.contractStore,
444
+ (addr, sel) => this.contractStore.getDebugFunctionName(addr, sel),
445
+ this.stateMachine.node,
406
446
  minRevertibleSideEffectCounter,
407
447
  );
408
448
 
@@ -415,7 +455,11 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
415
455
 
416
456
  const forkedWorldTrees = await this.stateMachine.synchronizer.nativeWorldStateService.fork();
417
457
 
418
- const contractsDB = new PublicContractsDB(new TXEPublicContractDataSource(blockNumber, this.contractStore));
458
+ const bindings = this.logger.getBindings();
459
+ const contractsDB = new PublicContractsDB(
460
+ new TXEPublicContractDataSource(blockNumber, this.contractStore),
461
+ bindings,
462
+ );
419
463
  const guardedMerkleTrees = new GuardedMerkleTreeOperations(forkedWorldTrees);
420
464
  const config = PublicSimulatorConfig.from({
421
465
  skipFeeEnforcement: true,
@@ -428,8 +472,10 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
428
472
  globals,
429
473
  guardedMerkleTrees,
430
474
  contractsDB,
431
- new CppPublicTxSimulator(guardedMerkleTrees, contractsDB, globals, config),
475
+ new CppPublicTxSimulator(guardedMerkleTrees, contractsDB, globals, config, bindings),
432
476
  new TestDateProvider(),
477
+ undefined,
478
+ createLogger('simulator:public-processor', bindings),
433
479
  );
434
480
 
435
481
  const tx = await Tx.create({
@@ -463,11 +509,17 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
463
509
  }
464
510
  }
465
511
 
512
+ // Walk the nested private-call tree and collect every offchain effect the transaction emitted.
513
+ // PXE stores these on each `PrivateCallExecutionResult` and they never reach TXE via the
514
+ // `aztec_utl_emitOffchainEffect` foreign-call path (that path only fires at the top-level), so
515
+ // we pull them out here and the RPC wrapper will hand them to `TXESession` for buffering.
516
+ const offchainEffects = collectNested([executionResult], r => r.offchainEffects.map(e => e.data));
517
+
466
518
  if (isStaticCall) {
467
519
  await checkpoint!.revert();
468
520
 
469
521
  await forkedWorldTrees.close();
470
- return executionResult.returnValues ?? [];
522
+ return { returnValues: executionResult.returnValues ?? [], offchainEffects };
471
523
  }
472
524
 
473
525
  const txEffect = TxEffect.empty();
@@ -489,26 +541,21 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
489
541
 
490
542
  await forkedWorldTrees.close();
491
543
 
492
- return executionResult.returnValues ?? [];
544
+ return { returnValues: executionResult.returnValues ?? [], offchainEffects };
493
545
  }
494
546
 
495
- async txePublicCallNewFlow(
496
- from: AztecAddress,
547
+ async publicCallNewFlow(
548
+ from: AztecAddress | undefined,
497
549
  targetContractAddress: AztecAddress,
498
550
  calldata: Fr[],
499
551
  isStaticCall: boolean,
552
+ gasSettings: GasSettings,
500
553
  ) {
501
554
  this.logger.verbose(
502
555
  `Executing public function ${await this.contractStore.getDebugFunctionName(targetContractAddress, FunctionSelector.fromField(calldata[0]))}@${targetContractAddress} isStaticCall=${isStaticCall}`,
503
556
  );
504
557
 
505
- const blockNumber = await this.txeGetNextBlockNumber();
506
-
507
- const gasLimits = new Gas(DEFAULT_DA_GAS_LIMIT, DEFAULT_L2_GAS_LIMIT);
508
-
509
- const teardownGasLimits = new Gas(DEFAULT_TEARDOWN_DA_GAS_LIMIT, DEFAULT_TEARDOWN_L2_GAS_LIMIT);
510
-
511
- const gasSettings = new GasSettings(gasLimits, teardownGasLimits, GasFees.empty(), GasFees.empty());
558
+ const blockNumber = await this.getNextBlockNumber();
512
559
 
513
560
  const txContext = new TxContext(this.chainId, this.version, gasSettings);
514
561
 
@@ -526,7 +573,11 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
526
573
 
527
574
  const forkedWorldTrees = await this.stateMachine.synchronizer.nativeWorldStateService.fork();
528
575
 
529
- const contractsDB = new PublicContractsDB(new TXEPublicContractDataSource(blockNumber, this.contractStore));
576
+ const bindings2 = this.logger.getBindings();
577
+ const contractsDB = new PublicContractsDB(
578
+ new TXEPublicContractDataSource(blockNumber, this.contractStore),
579
+ bindings2,
580
+ );
530
581
  const guardedMerkleTrees = new GuardedMerkleTreeOperations(forkedWorldTrees);
531
582
  const config = PublicSimulatorConfig.from({
532
583
  skipFeeEnforcement: true,
@@ -535,8 +586,16 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
535
586
  collectStatistics: false,
536
587
  collectCallMetadata: true,
537
588
  });
538
- const simulator = new CppPublicTxSimulator(guardedMerkleTrees, contractsDB, globals, config);
539
- const processor = new PublicProcessor(globals, guardedMerkleTrees, contractsDB, simulator, new TestDateProvider());
589
+ const simulator = new CppPublicTxSimulator(guardedMerkleTrees, contractsDB, globals, config, bindings2);
590
+ const processor = new PublicProcessor(
591
+ globals,
592
+ guardedMerkleTrees,
593
+ contractsDB,
594
+ simulator,
595
+ new TestDateProvider(),
596
+ undefined,
597
+ createLogger('simulator:public-processor', bindings2),
598
+ );
540
599
 
541
600
  // We're simulating a scenario in which private execution immediately enqueues a public call and halts. The private
542
601
  // kernel init would in this case inject a nullifier with the transaction request hash as a non-revertible
@@ -549,7 +608,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
549
608
  // may require producing reverts.
550
609
  const revertibleAccumulatedData = PrivateToPublicAccumulatedData.empty();
551
610
  revertibleAccumulatedData.publicCallRequests[0] = new PublicCallRequest(
552
- from,
611
+ from ?? AztecAddress.NULL_MSG_SENDER,
553
612
  targetContractAddress,
554
613
  isStaticCall,
555
614
  calldataHash,
@@ -567,7 +626,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
567
626
  constantData,
568
627
  /*gasUsed=*/ new Gas(0, 0),
569
628
  /*feePayer=*/ AztecAddress.zero(),
570
- /*includeByTimestamp=*/ 0n,
629
+ /*expirationTimestamp=*/ 0n,
571
630
  inputsForPublic,
572
631
  undefined,
573
632
  );
@@ -635,10 +694,12 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
635
694
  return returnValues ?? [];
636
695
  }
637
696
 
638
- async txeSimulateUtilityFunction(
697
+ async executeUtilityFunction(
639
698
  targetContractAddress: AztecAddress,
640
699
  functionSelector: FunctionSelector,
641
700
  args: Fr[],
701
+ jobId: string,
702
+ authorizedUtilityCallTargets: AztecAddress[],
642
703
  ) {
643
704
  const artifact = await this.contractStore.getFunctionArtifact(targetContractAddress, functionSelector);
644
705
  if (!artifact) {
@@ -646,25 +707,38 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
646
707
  }
647
708
 
648
709
  // Sync notes before executing utility function to discover notes from previous transactions
649
- await syncState(targetContractAddress, this.contractStore, functionSelector, async call => {
650
- await this.executeUtilityCall(call);
651
- });
652
-
653
- const call = new FunctionCall(
654
- artifact.name,
710
+ const blockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
711
+ await this.stateMachine.contractSyncService.ensureContractSynced(
655
712
  targetContractAddress,
656
713
  functionSelector,
657
- FunctionType.UTILITY,
658
- false,
659
- false,
660
- args,
661
- [],
714
+ async (call, execScopes) => {
715
+ await this.executeUtilityCall(call, execScopes, jobId);
716
+ },
717
+ blockHeader,
718
+ jobId,
719
+ await this.keyStore.getAccounts(),
662
720
  );
663
721
 
664
- return this.executeUtilityCall(call);
722
+ const call = FunctionCall.from({
723
+ name: artifact.name,
724
+ to: targetContractAddress,
725
+ selector: functionSelector,
726
+ type: FunctionType.UTILITY,
727
+ hideMsgSender: false,
728
+ isStatic: false,
729
+ args,
730
+ returnTypes: [],
731
+ });
732
+
733
+ return this.executeUtilityCall(call, await this.keyStore.getAccounts(), jobId, authorizedUtilityCallTargets);
665
734
  }
666
735
 
667
- private async executeUtilityCall(call: FunctionCall): Promise<Fr[]> {
736
+ private async executeUtilityCall(
737
+ call: FunctionCall,
738
+ scopes: AztecAddress[],
739
+ jobId: string,
740
+ authorizedUtilityCallTargets: AztecAddress[] = [],
741
+ ): Promise<Fr[]> {
668
742
  const entryPointArtifact = await this.contractStore.getFunctionArtifactWithDebugMetadata(call.to, call.selector);
669
743
  if (entryPointArtifact.functionType !== FunctionType.UTILITY) {
670
744
  throw new Error(`Cannot run ${entryPointArtifact.functionType} function as utility`);
@@ -677,24 +751,36 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
677
751
 
678
752
  try {
679
753
  const anchorBlockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
680
- const oracle = new UtilityExecutionOracle(
681
- call.to,
682
- [],
683
- [],
754
+ const simulator = new WASMSimulator();
755
+ const utilityExecutor = async (syncCall: FunctionCall, execScopes: AztecAddress[]) => {
756
+ await this.executeUtilityCall(syncCall, execScopes, jobId);
757
+ };
758
+ const oracle = new UtilityExecutionOracle({
759
+ contractAddress: call.to,
760
+ authWitnesses: [],
761
+ capsules: [],
684
762
  anchorBlockHeader,
685
- this.contractStore,
686
- this.noteStore,
687
- this.keyStore,
688
- this.addressStore,
689
- this.stateMachine.node,
690
- this.stateMachine.anchorBlockStore,
691
- this.recipientTaggingStore,
692
- this.senderAddressBookStore,
693
- this.capsuleStore,
694
- this.privateEventStore,
695
- this.jobId,
696
- );
697
- const acirExecutionResult = await new WASMSimulator()
763
+ contractStore: this.contractStore,
764
+ noteStore: this.noteStore,
765
+ keyStore: this.keyStore,
766
+ addressStore: this.addressStore,
767
+ aztecNode: this.stateMachine.node,
768
+ recipientTaggingStore: this.recipientTaggingStore,
769
+ senderAddressBookStore: this.senderAddressBookStore,
770
+ capsuleService: new CapsuleService(this.capsuleStore, scopes),
771
+ privateEventStore: this.privateEventStore,
772
+ messageContextService: this.stateMachine.messageContextService,
773
+ contractSyncService: this.stateMachine.contractSyncService,
774
+ l2TipsStore: this.stateMachine.node,
775
+ jobId,
776
+ scopes,
777
+ simulator,
778
+ hooks: composeHooks({
779
+ authorizeUtilityCall: this.buildAuthorizeUtilityCallHook('utility', authorizedUtilityCallTargets),
780
+ }),
781
+ utilityExecutor,
782
+ });
783
+ const acirExecutionResult = await simulator
698
784
  .executeUserCircuit(toACVMWitness(0, call.args), entryPointArtifact, new Oracle(oracle).toACIRCallback())
699
785
  .catch((err: Error) => {
700
786
  err.message = resolveAssertionMessageFromError(err, entryPointArtifact);
@@ -709,10 +795,10 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
709
795
  );
710
796
  });
711
797
 
712
- this.logger.verbose(`Utility simulation for ${call.to}.${call.selector} completed`);
798
+ this.logger.verbose(`Utility execution for ${call.to}.${call.selector} completed`);
713
799
  return witnessMapToFields(acirExecutionResult.returnWitness);
714
800
  } catch (err) {
715
- throw createSimulationError(err instanceof Error ? err : new Error('Unknown error during utility simulation'));
801
+ throw createSimulationError(err instanceof Error ? err : new Error('Unknown error during utility execution'));
716
802
  }
717
803
  }
718
804
 
@@ -725,4 +811,17 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
725
811
  const header = await this.stateMachine.node.getBlockHeader('latest');
726
812
  return header ? header.globalVariables.blockNumber : BlockNumber.ZERO;
727
813
  }
814
+
815
+ private buildAuthorizeUtilityCallHook(
816
+ callerContext: 'private' | 'utility',
817
+ authorizedTargets: AztecAddress[],
818
+ ): ExecutionHooks['authorizeUtilityCall'] | undefined {
819
+ if (authorizedTargets.length === 0) {
820
+ return undefined;
821
+ }
822
+ return req =>
823
+ Promise.resolve({
824
+ authorized: req.callerContext === callerContext && authorizedTargets.some(t => t.equals(req.target)),
825
+ });
826
+ }
728
827
  }