@aztec/txe 0.0.1-commit.2448fdb → 0.0.1-commit.2606882

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 (148) hide show
  1. package/dest/ContractClassRegistry-PII7Q556.js +3 -0
  2. package/dest/ContractClassRegistry-PII7Q556.js.map +7 -0
  3. package/dest/ContractInstanceRegistry-3MHLGFNZ.js +3 -0
  4. package/dest/ContractInstanceRegistry-3MHLGFNZ.js.map +7 -0
  5. package/dest/FeeJuice-TQV5AQK7.js +3 -0
  6. package/dest/FeeJuice-TQV5AQK7.js.map +7 -0
  7. package/dest/MultiCallEntrypoint-SBJUIJY7.js +3 -0
  8. package/dest/MultiCallEntrypoint-SBJUIJY7.js.map +7 -0
  9. package/dest/SchnorrAccount-MQXPIZ44.js +3 -0
  10. package/dest/SchnorrAccount-MQXPIZ44.js.map +7 -0
  11. package/dest/bin/check_txe_oracle_version.d.ts +2 -0
  12. package/dest/bin/check_txe_oracle_version.d.ts.map +1 -0
  13. package/dest/bin/check_txe_oracle_version.js +61 -0
  14. package/dest/bin/index.js +3 -30
  15. package/dest/bin/index.js.map +7 -0
  16. package/dest/bin/oracle_test_server.d.ts +3 -0
  17. package/dest/bin/oracle_test_server.d.ts.map +1 -0
  18. package/dest/bin/oracle_test_server.js +41 -0
  19. package/dest/chunk-3VX4QA7B.js +265 -0
  20. package/dest/chunk-3VX4QA7B.js.map +7 -0
  21. package/dest/chunk-BJVAAXNA.js +3 -0
  22. package/dest/chunk-BJVAAXNA.js.map +7 -0
  23. package/dest/chunk-FU3LBVE4.js +304 -0
  24. package/dest/chunk-FU3LBVE4.js.map +7 -0
  25. package/dest/constants.d.ts +5 -1
  26. package/dest/constants.d.ts.map +1 -1
  27. package/dest/constants.js +8 -0
  28. package/dest/dispatcher_pool.d.ts +67 -0
  29. package/dest/dispatcher_pool.d.ts.map +1 -0
  30. package/dest/dispatcher_pool.js +284 -0
  31. package/dest/index.d.ts +185 -7
  32. package/dest/index.d.ts.map +1 -1
  33. package/dest/index.js +70 -190
  34. package/dest/metafile.json +38352 -0
  35. package/dest/msgpackr_fr_extension.d.ts +2 -0
  36. package/dest/msgpackr_fr_extension.d.ts.map +1 -0
  37. package/dest/msgpackr_fr_extension.js +21 -0
  38. package/dest/oracle/interfaces.d.ts +33 -8
  39. package/dest/oracle/interfaces.d.ts.map +1 -1
  40. package/dest/oracle/test-resolver/fixtures.d.ts +43 -0
  41. package/dest/oracle/test-resolver/fixtures.d.ts.map +1 -0
  42. package/dest/oracle/test-resolver/fixtures.js +39 -0
  43. package/dest/oracle/test-resolver/index.d.ts +9 -0
  44. package/dest/oracle/test-resolver/index.d.ts.map +1 -0
  45. package/dest/oracle/test-resolver/index.js +33 -0
  46. package/dest/oracle/test-resolver/resolver.d.ts +34 -0
  47. package/dest/oracle/test-resolver/resolver.d.ts.map +1 -0
  48. package/dest/oracle/test-resolver/resolver.js +114 -0
  49. package/dest/oracle/txe_oracle_public_context.d.ts +26 -2
  50. package/dest/oracle/txe_oracle_public_context.d.ts.map +1 -1
  51. package/dest/oracle/txe_oracle_public_context.js +43 -1
  52. package/dest/oracle/txe_oracle_registry.d.ts +147 -0
  53. package/dest/oracle/txe_oracle_registry.d.ts.map +1 -0
  54. package/dest/oracle/txe_oracle_registry.js +562 -0
  55. package/dest/oracle/txe_oracle_top_level_context.d.ts +27 -13
  56. package/dest/oracle/txe_oracle_top_level_context.d.ts.map +1 -1
  57. package/dest/oracle/txe_oracle_top_level_context.js +129 -52
  58. package/dest/oracle/txe_oracle_version.d.ts +17 -0
  59. package/dest/oracle/txe_oracle_version.d.ts.map +1 -0
  60. package/dest/oracle/txe_oracle_version.js +14 -0
  61. package/dest/oracle/txe_private_execution_oracle.d.ts +17 -0
  62. package/dest/oracle/txe_private_execution_oracle.d.ts.map +1 -0
  63. package/dest/oracle/txe_private_execution_oracle.js +15 -0
  64. package/dest/rpc_server.d.ts +14 -0
  65. package/dest/rpc_server.d.ts.map +1 -0
  66. package/dest/rpc_server.js +78 -0
  67. package/dest/rpc_translator.d.ts +96 -266
  68. package/dest/rpc_translator.d.ts.map +1 -1
  69. package/dest/rpc_translator.js +699 -731
  70. package/dest/server.bundle.js +3 -0
  71. package/dest/server.bundle.js.map +7 -0
  72. package/dest/state_machine/archiver.d.ts +4 -3
  73. package/dest/state_machine/archiver.d.ts.map +1 -1
  74. package/dest/state_machine/archiver.js +26 -15
  75. package/dest/state_machine/dummy_p2p_client.d.ts +13 -7
  76. package/dest/state_machine/dummy_p2p_client.d.ts.map +1 -1
  77. package/dest/state_machine/dummy_p2p_client.js +16 -4
  78. package/dest/state_machine/global_variable_builder.d.ts +9 -4
  79. package/dest/state_machine/global_variable_builder.d.ts.map +1 -1
  80. package/dest/state_machine/global_variable_builder.js +9 -3
  81. package/dest/state_machine/index.d.ts +4 -2
  82. package/dest/state_machine/index.d.ts.map +1 -1
  83. package/dest/state_machine/index.js +17 -3
  84. package/dest/state_machine/mock_epoch_cache.d.ts +16 -3
  85. package/dest/state_machine/mock_epoch_cache.d.ts.map +1 -1
  86. package/dest/state_machine/mock_epoch_cache.js +29 -2
  87. package/dest/state_machine/synchronizer.js +1 -1
  88. package/dest/txe_session.d.ts +75 -14
  89. package/dest/txe_session.d.ts.map +1 -1
  90. package/dest/txe_session.js +164 -38
  91. package/dest/utils/encoding.d.ts +191 -0
  92. package/dest/utils/encoding.d.ts.map +1 -0
  93. package/dest/{util → utils}/encoding.js +7 -2
  94. package/dest/{util → utils}/expected_failure_error.d.ts +1 -1
  95. package/dest/utils/expected_failure_error.d.ts.map +1 -0
  96. package/dest/{util → utils}/txe_account_store.d.ts +1 -1
  97. package/dest/utils/txe_account_store.d.ts.map +1 -0
  98. package/dest/utils/txe_artifact_resolver.d.ts +37 -0
  99. package/dest/utils/txe_artifact_resolver.d.ts.map +1 -0
  100. package/dest/utils/txe_artifact_resolver.js +161 -0
  101. package/dest/utils/txe_public_contract_data_source.d.ts +20 -0
  102. package/dest/utils/txe_public_contract_data_source.d.ts.map +1 -0
  103. package/dest/{util → utils}/txe_public_contract_data_source.js +1 -3
  104. package/dest/worker.bundle.js +3 -0
  105. package/dest/worker.bundle.js.map +7 -0
  106. package/dest/worker.d.ts +2 -0
  107. package/dest/worker.d.ts.map +1 -0
  108. package/dest/worker.js +92 -0
  109. package/package.json +38 -21
  110. package/src/bin/check_txe_oracle_version.ts +70 -0
  111. package/src/bin/index.ts +11 -2
  112. package/src/bin/oracle_test_server.ts +51 -0
  113. package/src/constants.ts +10 -0
  114. package/src/dispatcher_pool.ts +316 -0
  115. package/src/index.ts +97 -227
  116. package/src/msgpackr_fr_extension.ts +23 -0
  117. package/src/oracle/interfaces.ts +29 -7
  118. package/src/oracle/test-resolver/fixtures.ts +84 -0
  119. package/src/oracle/test-resolver/index.ts +45 -0
  120. package/src/oracle/test-resolver/resolver.ts +165 -0
  121. package/src/oracle/txe_oracle_public_context.ts +60 -0
  122. package/src/oracle/txe_oracle_registry.ts +401 -0
  123. package/src/oracle/txe_oracle_top_level_context.ts +157 -62
  124. package/src/oracle/txe_oracle_version.ts +17 -0
  125. package/src/oracle/txe_private_execution_oracle.ts +30 -0
  126. package/src/rpc_server.ts +87 -0
  127. package/src/rpc_translator.ts +774 -1043
  128. package/src/state_machine/archiver.ts +38 -16
  129. package/src/state_machine/dummy_p2p_client.ts +31 -11
  130. package/src/state_machine/global_variable_builder.ts +18 -3
  131. package/src/state_machine/index.ts +20 -5
  132. package/src/state_machine/mock_epoch_cache.ts +38 -3
  133. package/src/state_machine/synchronizer.ts +1 -1
  134. package/src/txe_session.ts +269 -39
  135. package/src/{util → utils}/encoding.ts +8 -2
  136. package/src/utils/txe_artifact_resolver.ts +217 -0
  137. package/src/{util → utils}/txe_public_contract_data_source.ts +0 -2
  138. package/src/worker.ts +98 -0
  139. package/dest/util/encoding.d.ts +0 -719
  140. package/dest/util/encoding.d.ts.map +0 -1
  141. package/dest/util/expected_failure_error.d.ts.map +0 -1
  142. package/dest/util/txe_account_store.d.ts.map +0 -1
  143. package/dest/util/txe_public_contract_data_source.d.ts +0 -20
  144. package/dest/util/txe_public_contract_data_source.d.ts.map +0 -1
  145. /package/dest/{util → utils}/expected_failure_error.js +0 -0
  146. /package/dest/{util → utils}/txe_account_store.js +0 -0
  147. /package/src/{util → utils}/expected_failure_error.ts +0 -0
  148. /package/src/{util → utils}/txe_account_store.ts +0 -0
@@ -1,7 +1,6 @@
1
1
  import {
2
2
  CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS,
3
- MAX_PROCESSABLE_DA_GAS_PER_CHECKPOINT,
4
- MAX_PROCESSABLE_L2_GAS,
3
+ MAX_PRIVATE_LOGS_PER_TX,
5
4
  NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP,
6
5
  } from '@aztec/constants';
7
6
  import { BlockNumber } from '@aztec/foundation/branded-types';
@@ -15,13 +14,14 @@ import {
15
14
  CapsuleService,
16
15
  CapsuleStore,
17
16
  type ContractStore,
18
- type ContractSyncService,
17
+ type ExecutionHooks,
19
18
  NoteStore,
20
19
  ORACLE_VERSION_MAJOR,
21
20
  PrivateEventStore,
22
21
  RecipientTaggingStore,
23
22
  SenderAddressBookStore,
24
23
  SenderTaggingStore,
24
+ composeHooks,
25
25
  enrichPublicSimulationError,
26
26
  } from '@aztec/pxe/server';
27
27
  import {
@@ -55,13 +55,7 @@ import { AuthWitness } from '@aztec/stdlib/auth-witness';
55
55
  import { PublicSimulatorConfig } from '@aztec/stdlib/avm';
56
56
  import { AztecAddress } from '@aztec/stdlib/aztec-address';
57
57
  import { type ContractInstanceWithAddress, computePartialAddress } from '@aztec/stdlib/contract';
58
- import {
59
- FALLBACK_TEARDOWN_DA_GAS_LIMIT,
60
- FALLBACK_TEARDOWN_L2_GAS_LIMIT,
61
- Gas,
62
- GasFees,
63
- GasSettings,
64
- } from '@aztec/stdlib/gas';
58
+ import { Gas, GasFees, GasSettings } from '@aztec/stdlib/gas';
65
59
  import { computeCalldataHash, computeProtocolNullifier, siloNullifier } from '@aztec/stdlib/hash';
66
60
  import {
67
61
  PartialPrivateTailPublicInputsForPublic,
@@ -69,6 +63,7 @@ import {
69
63
  PrivateToPublicAccumulatedData,
70
64
  PublicCallRequest,
71
65
  } from '@aztec/stdlib/kernel';
66
+ import { hashPublicKey } from '@aztec/stdlib/keys';
72
67
  import { ChonkProof } from '@aztec/stdlib/proofs';
73
68
  import { makeGlobalVariables } from '@aztec/stdlib/testing';
74
69
  import { MerkleTreeId } from '@aztec/stdlib/trees';
@@ -85,13 +80,14 @@ import {
85
80
  collectNested,
86
81
  } from '@aztec/stdlib/tx';
87
82
  import type { UInt64 } from '@aztec/stdlib/types';
88
- import { ForkCheckpoint } from '@aztec/world-state';
83
+ import { ForkCheckpoint } from '@aztec/world-state/native';
89
84
 
90
- import { DEFAULT_ADDRESS } from '../constants.js';
85
+ import { DEFAULT_ADDRESS, MAX_PRIVATE_EVENTS_PER_TXE_QUERY, MAX_PRIVATE_EVENT_LEN } from '../constants.js';
91
86
  import type { TXEStateMachine } from '../state_machine/index.js';
92
- import type { TXEAccountStore } from '../util/txe_account_store.js';
93
- import { TXEPublicContractDataSource } from '../util/txe_public_contract_data_source.js';
94
87
  import { getSingleTxBlockRequestHash, insertTxEffectIntoWorldTrees, makeTXEBlock } from '../utils/block_creation.js';
88
+ import type { TXEAccountStore } from '../utils/txe_account_store.js';
89
+ import type { TXEArtifactResolver } from '../utils/txe_artifact_resolver.js';
90
+ import { TXEPublicContractDataSource } from '../utils/txe_public_contract_data_source.js';
95
91
  import type { ITxeExecutionOracle } from './interfaces.js';
96
92
 
97
93
  export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracle {
@@ -116,7 +112,9 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
116
112
  private version: Fr,
117
113
  private chainId: Fr,
118
114
  private authwits: Map<string, AuthWitness>,
119
- private readonly contractSyncService: ContractSyncService,
115
+ private readonly artifactResolver: TXEArtifactResolver,
116
+ private readonly rootPath: string,
117
+ private readonly packageName: string,
120
118
  ) {
121
119
  this.logger = createLogger('txe:top_level_context');
122
120
  this.logger.debug('Entering Top Level Context');
@@ -149,7 +147,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
149
147
  }
150
148
 
151
149
  // We instruct users to debug contracts via this oracle, so it makes sense that they'd expect it to also work in tests
152
- log(level: number, message: string, fields: Fr[]): Promise<void> {
150
+ log(level: number, message: string, _fieldsSize: number, fields: Fr[]): Promise<void> {
153
151
  if (!LogLevels[level]) {
154
152
  throw new Error(`Invalid log level: ${level}`);
155
153
  }
@@ -172,12 +170,12 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
172
170
  }
173
171
 
174
172
  async getLastBlockTimestamp() {
175
- return (await this.stateMachine.node.getBlockHeader('latest'))!.globalVariables.timestamp;
173
+ return (await this.stateMachine.node.getBlockData('latest'))!.header.globalVariables.timestamp;
176
174
  }
177
175
 
178
176
  async getLastTxEffects() {
179
177
  const latestBlockNumber = await this.stateMachine.archiver.getBlockNumber();
180
- const block = await this.stateMachine.archiver.getBlock(latestBlockNumber);
178
+ const block = await this.stateMachine.archiver.getBlock({ number: latestBlockNumber });
181
179
 
182
180
  if (block!.body.txEffects.length != 1) {
183
181
  // Note that calls like env.mine() will result in blocks with no transactions, hitting this
@@ -186,7 +184,17 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
186
184
 
187
185
  const txEffects = block!.body.txEffects[0];
188
186
 
189
- return { txHash: txEffects.txHash, noteHashes: txEffects.noteHashes, nullifiers: txEffects.nullifiers };
187
+ const privateLogs = txEffects.privateLogs;
188
+ if (privateLogs.length > MAX_PRIVATE_LOGS_PER_TX) {
189
+ throw new Error(`${privateLogs.length} private logs exceed max ${MAX_PRIVATE_LOGS_PER_TX}`);
190
+ }
191
+
192
+ return {
193
+ txHash: txEffects.txHash,
194
+ noteHashes: txEffects.noteHashes,
195
+ nullifiers: txEffects.nullifiers,
196
+ privateLogs,
197
+ };
190
198
  }
191
199
 
192
200
  async syncContractNonOracleMethod(contractAddress: AztecAddress, scope: AztecAddress, jobId: string) {
@@ -209,7 +217,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
209
217
  }
210
218
 
211
219
  async getPrivateEvents(selector: EventSelector, contractAddress: AztecAddress, scope: AztecAddress) {
212
- return (
220
+ const events = (
213
221
  await this.privateEventStore.getPrivateEvents(selector, {
214
222
  contractAddress,
215
223
  scopes: [scope],
@@ -217,6 +225,15 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
217
225
  toBlock: (await this.getLastBlockNumber()) + 1,
218
226
  })
219
227
  ).map(e => e.packedEvent);
228
+
229
+ if (events.length > MAX_PRIVATE_EVENTS_PER_TXE_QUERY) {
230
+ throw new Error(`Array of length ${events.length} larger than maxLen ${MAX_PRIVATE_EVENTS_PER_TXE_QUERY}`);
231
+ }
232
+ if (events.some(e => e.length > MAX_PRIVATE_EVENT_LEN)) {
233
+ throw new Error(`Some private event has length larger than maxLen ${MAX_PRIVATE_EVENT_LEN}`);
234
+ }
235
+
236
+ return events;
220
237
  }
221
238
 
222
239
  async advanceBlocksBy(blocks: number) {
@@ -232,27 +249,71 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
232
249
  this.nextBlockTimestamp += duration;
233
250
  }
234
251
 
235
- async deploy(artifact: ContractArtifact, instance: ContractInstanceWithAddress, secret: Fr) {
252
+ private deploymentNullifier(address: AztecAddress): Promise<Fr> {
253
+ return siloNullifier(AztecAddress.fromNumber(CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS), address.toField());
254
+ }
255
+
256
+ async deploy(
257
+ contractPath: string,
258
+ initializer: string,
259
+ args: Fr[],
260
+ secret: Fr,
261
+ salt: Fr,
262
+ deployer: AztecAddress,
263
+ ): Promise<Fr[]> {
264
+ const { artifact, instance } = await this.artifactResolver.resolveDeployArtifact({
265
+ rootPath: this.rootPath,
266
+ packageName: this.packageName,
267
+ contractPath,
268
+ initializer,
269
+ args,
270
+ secret,
271
+ salt,
272
+ deployer,
273
+ });
274
+
236
275
  // Emit deployment nullifier
237
276
  await this.mineBlock({
238
- nullifiers: [
239
- await siloNullifier(
240
- AztecAddress.fromNumber(CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS),
241
- instance.address.toField(),
242
- ),
243
- ],
277
+ nullifiers: [await this.deploymentNullifier(instance.address)],
244
278
  });
245
279
 
246
280
  if (!secret.equals(Fr.ZERO)) {
247
- await this.addAccount(artifact, instance, secret);
281
+ await this.registerContractAndAddAccount(artifact, instance, secret);
248
282
  } else {
249
283
  await this.contractStore.addContractInstance(instance);
250
284
  await this.contractStore.addContractArtifact(artifact);
251
285
  this.logger.debug(`Deployed ${artifact.name} at ${instance.address}`);
252
286
  }
287
+
288
+ return [
289
+ instance.salt,
290
+ instance.deployer.toField(),
291
+ instance.currentContractClassId,
292
+ instance.initializationHash,
293
+ instance.immutablesHash,
294
+ ...instance.publicKeys.toFields(),
295
+ ];
296
+ }
297
+
298
+ /**
299
+ * Mines a single block containing only the deployment nullifiers for the contracts at the given addresses.
300
+ */
301
+ async mineDeploymentNullifiers(addresses: AztecAddress[]) {
302
+ await this.mineBlock({
303
+ nullifiers: await Promise.all(addresses.map(address => this.deploymentNullifier(address))),
304
+ });
253
305
  }
254
306
 
255
- async addAccount(artifact: ContractArtifact, instance: ContractInstanceWithAddress, secret: Fr) {
307
+ async addAccount(secret: Fr) {
308
+ const { artifact, instance } = await this.artifactResolver.resolveAccountArtifact(secret);
309
+ return this.registerContractAndAddAccount(artifact, instance, secret);
310
+ }
311
+
312
+ private async registerContractAndAddAccount(
313
+ artifact: ContractArtifact,
314
+ instance: ContractInstanceWithAddress,
315
+ secret: Fr,
316
+ ) {
256
317
  const partialAddress = await computePartialAddress(instance);
257
318
 
258
319
  this.logger.debug(`Deployed ${artifact.name} at ${instance.address}`);
@@ -279,12 +340,13 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
279
340
 
280
341
  async addAuthWitness(address: AztecAddress, messageHash: Fr) {
281
342
  const account = await this.accountStore.getAccount(address);
282
- const privateKey = await this.keyStore.getMasterSecretKey(account.publicKeys.masterIncomingViewingPublicKey);
343
+ const ivpkMHash = await hashPublicKey(account.publicKeys.ivpkM);
344
+ const privateKey = await this.keyStore.getMasterSecretKey(ivpkMHash);
283
345
 
284
346
  const schnorr = new Schnorr();
285
- const signature = await schnorr.constructSignature(messageHash.toBuffer(), privateKey);
347
+ const signature = await schnorr.constructSignature(messageHash, privateKey);
286
348
 
287
- const authWitness = new AuthWitness(messageHash, [...signature.toBuffer()]);
349
+ const authWitness = new AuthWitness(messageHash, signature.toLimbFields());
288
350
 
289
351
  this.authwits.set(authWitness.requestHash.toString(), authWitness);
290
352
  }
@@ -315,13 +377,16 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
315
377
  }
316
378
 
317
379
  async privateCallNewFlow(
318
- from: AztecAddress,
380
+ from: AztecAddress | undefined,
319
381
  targetContractAddress: AztecAddress = AztecAddress.zero(),
320
382
  functionSelector: FunctionSelector = FunctionSelector.empty(),
321
383
  args: Fr[],
322
384
  argsHash: Fr = Fr.zero(),
323
385
  isStaticCall: boolean = false,
386
+ additionalScopes: AztecAddress[] = [],
324
387
  jobId: string,
388
+ authorizedUtilityCallTargets: AztecAddress[],
389
+ gasSettings: GasSettings,
325
390
  ) {
326
391
  this.logger.verbose(
327
392
  `Executing external function ${await this.contractStore.getDebugFunctionName(targetContractAddress, functionSelector)}@${targetContractAddress} isStaticCall=${isStaticCall}`,
@@ -335,9 +400,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
335
400
  throw new Error(message);
336
401
  }
337
402
 
338
- // When `from` is the zero address (e.g. when deploying a new account contract), we return an
339
- // empty scope list which acts as deny-all: no notes are visible and no keys are accessible.
340
- const effectiveScopes = from.isZero() ? [] : [from];
403
+ const scopes = from === undefined ? additionalScopes : [from, ...additionalScopes];
341
404
 
342
405
  // Sync notes before executing private function to discover notes from previous transactions
343
406
  const utilityExecutor = async (call: FunctionCall, execScopes: AztecAddress[]) => {
@@ -351,16 +414,13 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
351
414
  utilityExecutor,
352
415
  blockHeader,
353
416
  jobId,
354
- effectiveScopes,
417
+ scopes,
355
418
  );
356
419
 
357
420
  const blockNumber = await this.getNextBlockNumber();
358
421
 
359
- const callContext = new CallContext(from, targetContractAddress, functionSelector, isStaticCall);
360
-
361
- const gasLimits = new Gas(MAX_PROCESSABLE_DA_GAS_PER_CHECKPOINT, MAX_PROCESSABLE_L2_GAS);
362
- const teardownGasLimits = new Gas(FALLBACK_TEARDOWN_DA_GAS_LIMIT, FALLBACK_TEARDOWN_L2_GAS_LIMIT);
363
- const gasSettings = new GasSettings(gasLimits, teardownGasLimits, GasFees.empty(), GasFees.empty());
422
+ const msgSender = from ?? AztecAddress.NULL_MSG_SENDER;
423
+ const callContext = new CallContext(msgSender, targetContractAddress, functionSelector, isStaticCall);
364
424
 
365
425
  const txContext = new TxContext(this.chainId, this.version, gasSettings);
366
426
 
@@ -394,19 +454,25 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
394
454
  senderTaggingStore: this.senderTaggingStore,
395
455
  recipientTaggingStore: this.recipientTaggingStore,
396
456
  senderAddressBookStore: this.senderAddressBookStore,
397
- capsuleService: new CapsuleService(this.capsuleStore, effectiveScopes),
457
+ capsuleService: new CapsuleService(this.capsuleStore, scopes),
398
458
  privateEventStore: this.privateEventStore,
399
459
  contractSyncService: this.stateMachine.contractSyncService,
400
460
  jobId,
401
461
  totalPublicCalldataCount: 0,
402
462
  sideEffectCounter: minRevertibleSideEffectCounter,
403
- scopes: effectiveScopes,
463
+ scopes,
404
464
  // In TXE, the typical transaction entrypoint is skipped, so we need to simulate the actions that such a
405
465
  // contract would perform, including setting senderForTags.
406
466
  senderForTags: from,
407
467
  simulator,
408
468
  messageContextService: this.stateMachine.messageContextService,
409
- l2TipsStore: this.stateMachine.node,
469
+ l2TipsStore: this.stateMachine.l2TipsProvider,
470
+ hooks: composeHooks({
471
+ authorizeUtilityCall: this.buildAuthorizeUtilityCallHook(
472
+ isStaticCall ? 'private view' : 'private',
473
+ authorizedUtilityCallTargets,
474
+ ),
475
+ }),
410
476
  });
411
477
 
412
478
  // 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.
@@ -514,11 +580,17 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
514
580
  }
515
581
  }
516
582
 
583
+ // Walk the nested private-call tree and collect every offchain effect the transaction emitted.
584
+ // PXE stores these on each `PrivateCallExecutionResult` and they never reach TXE via the
585
+ // `aztec_utl_emitOffchainEffect` foreign-call path (that path only fires at the top-level), so
586
+ // we pull them out here and the RPC wrapper will hand them to `TXESession` for buffering.
587
+ const offchainEffects = collectNested([executionResult], r => r.offchainEffects.map(e => e.data));
588
+
517
589
  if (isStaticCall) {
518
590
  await checkpoint!.revert();
519
591
 
520
592
  await forkedWorldTrees.close();
521
- return executionResult.returnValues ?? [];
593
+ return { returnValues: executionResult.returnValues ?? [], offchainEffects };
522
594
  }
523
595
 
524
596
  const txEffect = TxEffect.empty();
@@ -540,14 +612,15 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
540
612
 
541
613
  await forkedWorldTrees.close();
542
614
 
543
- return executionResult.returnValues ?? [];
615
+ return { returnValues: executionResult.returnValues ?? [], offchainEffects };
544
616
  }
545
617
 
546
618
  async publicCallNewFlow(
547
- from: AztecAddress,
619
+ from: AztecAddress | undefined,
548
620
  targetContractAddress: AztecAddress,
549
621
  calldata: Fr[],
550
622
  isStaticCall: boolean,
623
+ gasSettings: GasSettings,
551
624
  ) {
552
625
  this.logger.verbose(
553
626
  `Executing public function ${await this.contractStore.getDebugFunctionName(targetContractAddress, FunctionSelector.fromField(calldata[0]))}@${targetContractAddress} isStaticCall=${isStaticCall}`,
@@ -555,12 +628,6 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
555
628
 
556
629
  const blockNumber = await this.getNextBlockNumber();
557
630
 
558
- const gasLimits = new Gas(MAX_PROCESSABLE_DA_GAS_PER_CHECKPOINT, MAX_PROCESSABLE_L2_GAS);
559
-
560
- const teardownGasLimits = new Gas(FALLBACK_TEARDOWN_DA_GAS_LIMIT, FALLBACK_TEARDOWN_L2_GAS_LIMIT);
561
-
562
- const gasSettings = new GasSettings(gasLimits, teardownGasLimits, GasFees.empty(), GasFees.empty());
563
-
564
631
  const txContext = new TxContext(this.chainId, this.version, gasSettings);
565
632
 
566
633
  const anchorBlockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
@@ -612,7 +679,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
612
679
  // may require producing reverts.
613
680
  const revertibleAccumulatedData = PrivateToPublicAccumulatedData.empty();
614
681
  revertibleAccumulatedData.publicCallRequests[0] = new PublicCallRequest(
615
- from,
682
+ from ?? AztecAddress.NULL_MSG_SENDER,
616
683
  targetContractAddress,
617
684
  isStaticCall,
618
685
  calldataHash,
@@ -703,6 +770,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
703
770
  functionSelector: FunctionSelector,
704
771
  args: Fr[],
705
772
  jobId: string,
773
+ authorizedUtilityCallTargets: AztecAddress[],
706
774
  ) {
707
775
  const artifact = await this.contractStore.getFunctionArtifact(targetContractAddress, functionSelector);
708
776
  if (!artifact) {
@@ -733,10 +801,15 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
733
801
  returnTypes: [],
734
802
  });
735
803
 
736
- return this.executeUtilityCall(call, await this.keyStore.getAccounts(), jobId);
804
+ return this.executeUtilityCall(call, await this.keyStore.getAccounts(), jobId, authorizedUtilityCallTargets);
737
805
  }
738
806
 
739
- private async executeUtilityCall(call: FunctionCall, scopes: AztecAddress[], jobId: string): Promise<Fr[]> {
807
+ private async executeUtilityCall(
808
+ call: FunctionCall,
809
+ scopes: AztecAddress[],
810
+ jobId: string,
811
+ authorizedUtilityCallTargets: AztecAddress[] = [],
812
+ ): Promise<Fr[]> {
740
813
  const entryPointArtifact = await this.contractStore.getFunctionArtifactWithDebugMetadata(call.to, call.selector);
741
814
  if (entryPointArtifact.functionType !== FunctionType.UTILITY) {
742
815
  throw new Error(`Cannot run ${entryPointArtifact.functionType} function as utility`);
@@ -749,6 +822,10 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
749
822
 
750
823
  try {
751
824
  const anchorBlockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
825
+ const simulator = new WASMSimulator();
826
+ const utilityExecutor = async (syncCall: FunctionCall, execScopes: AztecAddress[]) => {
827
+ await this.executeUtilityCall(syncCall, execScopes, jobId);
828
+ };
752
829
  const oracle = new UtilityExecutionOracle({
753
830
  contractAddress: call.to,
754
831
  authWitnesses: [],
@@ -764,12 +841,17 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
764
841
  capsuleService: new CapsuleService(this.capsuleStore, scopes),
765
842
  privateEventStore: this.privateEventStore,
766
843
  messageContextService: this.stateMachine.messageContextService,
767
- contractSyncService: this.contractSyncService,
768
- l2TipsStore: this.stateMachine.node,
844
+ contractSyncService: this.stateMachine.contractSyncService,
845
+ l2TipsStore: this.stateMachine.l2TipsProvider,
769
846
  jobId,
770
847
  scopes,
848
+ simulator,
849
+ hooks: composeHooks({
850
+ authorizeUtilityCall: this.buildAuthorizeUtilityCallHook('utility', authorizedUtilityCallTargets),
851
+ }),
852
+ utilityExecutor,
771
853
  });
772
- const acirExecutionResult = await new WASMSimulator()
854
+ const acirExecutionResult = await simulator
773
855
  .executeUserCircuit(toACVMWitness(0, call.args), entryPointArtifact, new Oracle(oracle).toACIRCallback())
774
856
  .catch((err: Error) => {
775
857
  err.message = resolveAssertionMessageFromError(err, entryPointArtifact);
@@ -797,7 +879,20 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
797
879
  }
798
880
 
799
881
  private async getLastBlockNumber(): Promise<BlockNumber> {
800
- const header = await this.stateMachine.node.getBlockHeader('latest');
801
- return header ? header.globalVariables.blockNumber : BlockNumber.ZERO;
882
+ const block = await this.stateMachine.node.getBlock('latest');
883
+ return block ? block.header.globalVariables.blockNumber : BlockNumber.ZERO;
884
+ }
885
+
886
+ private buildAuthorizeUtilityCallHook(
887
+ callerContext: 'private' | 'private view' | 'utility',
888
+ authorizedTargets: AztecAddress[],
889
+ ): ExecutionHooks['authorizeUtilityCall'] | undefined {
890
+ if (authorizedTargets.length === 0) {
891
+ return undefined;
892
+ }
893
+ return req =>
894
+ Promise.resolve({
895
+ authorized: req.callerContext === callerContext && authorizedTargets.some(t => t.equals(req.target)),
896
+ });
802
897
  }
803
898
  }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * The TXE oracle version constants are used to check that the oracle interface used for tests is in sync between
3
+ * TXE and Aztec.nr. This is separate from the contract oracle version in `pxe/src/oracle_version.ts`, which covers
4
+ * oracles used during contract execution by PXE.
5
+ *
6
+ * The Noir counterparts are in `noir-projects/aztec-nr/aztec/src/test/helpers/txe_oracles.nr`.
7
+ */
8
+ export const TXE_ORACLE_VERSION_MAJOR = 1;
9
+ export const TXE_ORACLE_VERSION_MINOR = 2;
10
+
11
+ /**
12
+ * This hash is computed from the TXE oracle interfaces (IAvmExecutionOracle and ITxeExecutionOracle) and is used to
13
+ * detect when those interfaces change. When it does, bump:
14
+ * - TXE_ORACLE_VERSION_MAJOR (and reset MINOR to 0) for breaking changes, or
15
+ * - TXE_ORACLE_VERSION_MINOR for additive changes (new oracle method added).
16
+ */
17
+ export const TXE_ORACLE_INTERFACE_HASH = '9e5f6ad5fd170d1de5ddd417f19cce47b17382567e08360dc6a783154828e218';
@@ -0,0 +1,30 @@
1
+ import type { Fr } from '@aztec/foundation/curves/bn254';
2
+ import { PrivateExecutionOracle } from '@aztec/pxe/simulator';
3
+ import type { FunctionSelector } from '@aztec/stdlib/abi';
4
+ import type { AztecAddress } from '@aztec/stdlib/aztec-address';
5
+
6
+ /**
7
+ * TXE-specific subclass of PrivateExecutionOracle that forbids operations not supported in
8
+ * TestEnvironment::private_context. TXE uses dedicated oracle flows (e.g. private_call) instead.
9
+ */
10
+ export class TXEPrivateExecutionOracle extends PrivateExecutionOracle {
11
+ override callPrivateFunction(
12
+ _targetContractAddress: AztecAddress,
13
+ _functionSelector: FunctionSelector,
14
+ _argsHash: Fr,
15
+ _sideEffectCounter: number,
16
+ _isStaticCall: boolean,
17
+ ): Promise<{ endSideEffectCounter: Fr; returnsHash: Fr }> {
18
+ throw new Error(
19
+ 'Contract calls are forbidden inside a `TestEnvironment::private_context`, use `private_call` instead',
20
+ );
21
+ }
22
+
23
+ override assertValidPublicCalldata(_calldataHash: Fr): Promise<void> {
24
+ throw new Error('Enqueueing public calls is not supported in TestEnvironment::private_context');
25
+ }
26
+
27
+ override notifyRevertiblePhaseStart(_minRevertibleSideEffectCounter: number): Promise<void> {
28
+ throw new Error('Enqueueing public calls is not supported in TestEnvironment::private_context');
29
+ }
30
+ }
@@ -0,0 +1,87 @@
1
+ import { createSafeJsonRpcServer } from '@aztec/foundation/json-rpc/server';
2
+ import type { Logger } from '@aztec/foundation/log';
3
+
4
+ import type { Socket } from 'node:net';
5
+
6
+ import { TXEDispatcherPool, buildSharedContractStore } from './dispatcher_pool.js';
7
+ import { TXEDispatcher, TXEDispatcherApiSchema } from './index.js';
8
+
9
+ /**
10
+ * Symbol used to tag an incoming TCP socket with the `session_id` it has been associated with.
11
+ * Hidden under a Symbol so we don't risk colliding with anything koa or http core adds.
12
+ */
13
+ const SESSION_SYMBOL = Symbol('txeSessionId');
14
+
15
+ type TaggedSocket = Socket & { [SESSION_SYMBOL]?: number };
16
+
17
+ /**
18
+ * Creates the TXE RPC server. With `TXE_WORKERS=1` oracle calls run on the main thread (no
19
+ * worker_threads, no IPC overhead). With any other value oracle calls are
20
+ * routed to a pool of worker threads sized to that value, sticky by `session_id`.
21
+ *
22
+ * Each incoming TCP socket is tagged with the `session_id` of the first oracle call it carries —
23
+ * nargo uses one HTTP client per test, so the socket-to-session mapping is 1:1. When the socket
24
+ * closes (end of test), the dispatcher disposes the session and frees its world state + LMDB.
25
+ *
26
+ * Lives in its own module so the worker bundle does not pull in the HTTP server stack.
27
+ */
28
+ export async function createTXERpcServer(logger: Logger) {
29
+ const workerCount = Number(process.env.TXE_WORKERS);
30
+ let dispatcher: TXEDispatcher | TXEDispatcherPool;
31
+ if (workerCount === 1) {
32
+ const { dataDir, schnorrClassId } = await buildSharedContractStore();
33
+ dispatcher = new TXEDispatcher(logger, { contractStoreSourceDir: dataDir, schnorrClassId });
34
+ } else {
35
+ dispatcher = new TXEDispatcherPool(logger, {
36
+ workers: Number.isFinite(workerCount) && workerCount > 1 ? workerCount : undefined,
37
+ });
38
+ }
39
+ const server = createSafeJsonRpcServer(dispatcher, TXEDispatcherApiSchema, {
40
+ http200OnError: true,
41
+ middlewares: [
42
+ async (ctx, next) => {
43
+ // The body parser runs further down the chain, so `ctx.request.body` is populated only
44
+ // after `next()` resolves.
45
+ await next();
46
+ const socket = ctx.req.socket as TaggedSocket;
47
+ if (socket[SESSION_SYMBOL] !== undefined) {
48
+ return;
49
+ }
50
+ const body = (ctx.request as { body?: unknown }).body;
51
+ const sessionId =
52
+ body && typeof body === 'object' && 'params' in body
53
+ ? extractSessionId((body as { params: unknown }).params)
54
+ : undefined;
55
+ if (sessionId === undefined) {
56
+ return;
57
+ }
58
+ socket[SESSION_SYMBOL] = sessionId;
59
+ logger.debug(`Tagged socket with session`, {
60
+ sessionId,
61
+ remoteAddress: socket.remoteAddress,
62
+ remotePort: socket.remotePort,
63
+ });
64
+ socket.once('close', () => {
65
+ logger.debug(`Disposing session on socket close`, { sessionId });
66
+ void dispatcher.disposeSession(sessionId);
67
+ });
68
+ },
69
+ ],
70
+ });
71
+ return server;
72
+ }
73
+
74
+ // Extracts `session_id` from a JSON-RPC `params` array. Always a `number` because session_id
75
+ // comes off `JSON.parse`, which never produces BigInt; values above MAX_SAFE_INTEGER lose
76
+ // precision but still work as a Map key.
77
+ function extractSessionId(params: unknown): number | undefined {
78
+ if (!Array.isArray(params) || params.length === 0) {
79
+ return undefined;
80
+ }
81
+ const first = params[0];
82
+ if (first && typeof first === 'object' && 'session_id' in first) {
83
+ const sid = (first as { session_id: unknown }).session_id;
84
+ return typeof sid === 'number' ? sid : undefined;
85
+ }
86
+ return undefined;
87
+ }