@aztec/txe 0.0.1-commit.96dac018d → 0.0.1-commit.993d240

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 (126) hide show
  1. package/dest/ContractClassRegistry-KKNT527R.js +3 -0
  2. package/dest/ContractClassRegistry-KKNT527R.js.map +7 -0
  3. package/dest/ContractInstanceRegistry-GSKBNL3Q.js +3 -0
  4. package/dest/ContractInstanceRegistry-GSKBNL3Q.js.map +7 -0
  5. package/dest/FeeJuice-GUJ5O6GB.js +3 -0
  6. package/dest/FeeJuice-GUJ5O6GB.js.map +7 -0
  7. package/dest/MultiCallEntrypoint-ZQPCKVKL.js +3 -0
  8. package/dest/MultiCallEntrypoint-ZQPCKVKL.js.map +7 -0
  9. package/dest/SchnorrAccount-QIXE5DXA.js +3 -0
  10. package/dest/SchnorrAccount-QIXE5DXA.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 +32 -0
  14. package/dest/bin/index.js +3 -30
  15. package/dest/bin/index.js.map +7 -0
  16. package/dest/chunk-BJVAAXNA.js +3 -0
  17. package/dest/chunk-BJVAAXNA.js.map +7 -0
  18. package/dest/chunk-DF6RMOC6.js +304 -0
  19. package/dest/chunk-DF6RMOC6.js.map +7 -0
  20. package/dest/chunk-PPJTNNUG.js +265 -0
  21. package/dest/chunk-PPJTNNUG.js.map +7 -0
  22. package/dest/constants.d.ts +3 -1
  23. package/dest/constants.d.ts.map +1 -1
  24. package/dest/constants.js +5 -0
  25. package/dest/dispatcher_pool.d.ts +67 -0
  26. package/dest/dispatcher_pool.d.ts.map +1 -0
  27. package/dest/dispatcher_pool.js +284 -0
  28. package/dest/index.d.ts +188 -7
  29. package/dest/index.d.ts.map +1 -1
  30. package/dest/index.js +160 -119
  31. package/dest/metafile.json +38147 -0
  32. package/dest/msgpackr_fr_extension.d.ts +2 -0
  33. package/dest/msgpackr_fr_extension.d.ts.map +1 -0
  34. package/dest/msgpackr_fr_extension.js +21 -0
  35. package/dest/oracle/interfaces.d.ts +39 -28
  36. package/dest/oracle/interfaces.d.ts.map +1 -1
  37. package/dest/oracle/txe_oracle_public_context.d.ts +18 -13
  38. package/dest/oracle/txe_oracle_public_context.d.ts.map +1 -1
  39. package/dest/oracle/txe_oracle_public_context.js +27 -12
  40. package/dest/oracle/txe_oracle_top_level_context.d.ts +36 -20
  41. package/dest/oracle/txe_oracle_top_level_context.d.ts.map +1 -1
  42. package/dest/oracle/txe_oracle_top_level_context.js +125 -66
  43. package/dest/oracle/txe_private_execution_oracle.d.ts +17 -0
  44. package/dest/oracle/txe_private_execution_oracle.d.ts.map +1 -0
  45. package/dest/oracle/txe_private_execution_oracle.js +15 -0
  46. package/dest/rpc_server.d.ts +14 -0
  47. package/dest/rpc_server.d.ts.map +1 -0
  48. package/dest/rpc_server.js +78 -0
  49. package/dest/rpc_translator.d.ts +139 -83
  50. package/dest/rpc_translator.d.ts.map +1 -1
  51. package/dest/rpc_translator.js +568 -244
  52. package/dest/server.bundle.js +3 -0
  53. package/dest/server.bundle.js.map +7 -0
  54. package/dest/state_machine/archiver.d.ts +4 -4
  55. package/dest/state_machine/archiver.d.ts.map +1 -1
  56. package/dest/state_machine/archiver.js +22 -20
  57. package/dest/state_machine/dummy_p2p_client.d.ts +13 -7
  58. package/dest/state_machine/dummy_p2p_client.d.ts.map +1 -1
  59. package/dest/state_machine/dummy_p2p_client.js +16 -4
  60. package/dest/state_machine/global_variable_builder.d.ts +9 -4
  61. package/dest/state_machine/global_variable_builder.d.ts.map +1 -1
  62. package/dest/state_machine/global_variable_builder.js +9 -3
  63. package/dest/state_machine/index.d.ts +7 -3
  64. package/dest/state_machine/index.d.ts.map +1 -1
  65. package/dest/state_machine/index.js +23 -5
  66. package/dest/state_machine/mock_epoch_cache.d.ts +18 -3
  67. package/dest/state_machine/mock_epoch_cache.d.ts.map +1 -1
  68. package/dest/state_machine/mock_epoch_cache.js +35 -2
  69. package/dest/state_machine/synchronizer.d.ts +5 -5
  70. package/dest/state_machine/synchronizer.d.ts.map +1 -1
  71. package/dest/state_machine/synchronizer.js +4 -4
  72. package/dest/txe_oracle_version.d.ts +17 -0
  73. package/dest/txe_oracle_version.d.ts.map +1 -0
  74. package/dest/txe_oracle_version.js +14 -0
  75. package/dest/txe_session.d.ts +64 -6
  76. package/dest/txe_session.d.ts.map +1 -1
  77. package/dest/txe_session.js +159 -31
  78. package/dest/utils/encoding.d.ts +191 -0
  79. package/dest/utils/encoding.d.ts.map +1 -0
  80. package/dest/{util → utils}/encoding.js +7 -2
  81. package/dest/{util → utils}/expected_failure_error.d.ts +1 -1
  82. package/dest/utils/expected_failure_error.d.ts.map +1 -0
  83. package/dest/{util → utils}/txe_account_store.d.ts +1 -1
  84. package/dest/utils/txe_account_store.d.ts.map +1 -0
  85. package/dest/utils/txe_public_contract_data_source.d.ts +20 -0
  86. package/dest/utils/txe_public_contract_data_source.d.ts.map +1 -0
  87. package/dest/{util → utils}/txe_public_contract_data_source.js +1 -3
  88. package/dest/worker.bundle.js +3 -0
  89. package/dest/worker.bundle.js.map +7 -0
  90. package/dest/worker.d.ts +2 -0
  91. package/dest/worker.d.ts.map +1 -0
  92. package/dest/worker.js +92 -0
  93. package/package.json +37 -21
  94. package/src/bin/check_txe_oracle_version.ts +37 -0
  95. package/src/bin/index.ts +11 -2
  96. package/src/constants.ts +6 -0
  97. package/src/dispatcher_pool.ts +316 -0
  98. package/src/index.ts +178 -126
  99. package/src/msgpackr_fr_extension.ts +23 -0
  100. package/src/oracle/interfaces.ts +43 -34
  101. package/src/oracle/txe_oracle_public_context.ts +42 -12
  102. package/src/oracle/txe_oracle_top_level_context.ts +146 -77
  103. package/src/oracle/txe_private_execution_oracle.ts +30 -0
  104. package/src/rpc_server.ts +87 -0
  105. package/src/rpc_translator.ts +647 -327
  106. package/src/state_machine/archiver.ts +33 -18
  107. package/src/state_machine/dummy_p2p_client.ts +31 -11
  108. package/src/state_machine/global_variable_builder.ts +18 -3
  109. package/src/state_machine/index.ts +25 -6
  110. package/src/state_machine/mock_epoch_cache.ts +46 -3
  111. package/src/state_machine/synchronizer.ts +5 -5
  112. package/src/txe_oracle_version.ts +17 -0
  113. package/src/txe_session.ts +253 -33
  114. package/src/{util → utils}/encoding.ts +8 -2
  115. package/src/{util → utils}/txe_public_contract_data_source.ts +0 -2
  116. package/src/worker.ts +98 -0
  117. package/dest/util/encoding.d.ts +0 -720
  118. package/dest/util/encoding.d.ts.map +0 -1
  119. package/dest/util/expected_failure_error.d.ts.map +0 -1
  120. package/dest/util/txe_account_store.d.ts.map +0 -1
  121. package/dest/util/txe_public_contract_data_source.d.ts +0 -20
  122. package/dest/util/txe_public_contract_data_source.d.ts.map +0 -1
  123. /package/dest/{util → utils}/expected_failure_error.js +0 -0
  124. /package/dest/{util → utils}/txe_account_store.js +0 -0
  125. /package/src/{util → utils}/expected_failure_error.ts +0 -0
  126. /package/src/{util → utils}/txe_account_store.ts +0 -0
@@ -1,9 +1,6 @@
1
1
  import {
2
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,
3
+ MAX_PRIVATE_LOGS_PER_TX,
7
4
  NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP,
8
5
  } from '@aztec/constants';
9
6
  import { BlockNumber } from '@aztec/foundation/branded-types';
@@ -12,17 +9,19 @@ import { Fr } from '@aztec/foundation/curves/bn254';
12
9
  import { LogLevels, type Logger, applyStringFormatting, createLogger } from '@aztec/foundation/log';
13
10
  import { TestDateProvider } from '@aztec/foundation/timer';
14
11
  import type { KeyStore } from '@aztec/key-store';
15
- import type { AccessScopes } from '@aztec/pxe/client/lazy';
16
12
  import {
17
13
  AddressStore,
14
+ CapsuleService,
18
15
  CapsuleStore,
19
16
  type ContractStore,
17
+ type ExecutionHooks,
20
18
  NoteStore,
21
- ORACLE_VERSION,
19
+ ORACLE_VERSION_MAJOR,
22
20
  PrivateEventStore,
23
21
  RecipientTaggingStore,
24
22
  SenderAddressBookStore,
25
23
  SenderTaggingStore,
24
+ composeHooks,
26
25
  enrichPublicSimulationError,
27
26
  } from '@aztec/pxe/server';
28
27
  import {
@@ -64,6 +63,7 @@ import {
64
63
  PrivateToPublicAccumulatedData,
65
64
  PublicCallRequest,
66
65
  } from '@aztec/stdlib/kernel';
66
+ import { hashPublicKey } from '@aztec/stdlib/keys';
67
67
  import { ChonkProof } from '@aztec/stdlib/proofs';
68
68
  import { makeGlobalVariables } from '@aztec/stdlib/testing';
69
69
  import { MerkleTreeId } from '@aztec/stdlib/trees';
@@ -80,13 +80,13 @@ import {
80
80
  collectNested,
81
81
  } from '@aztec/stdlib/tx';
82
82
  import type { UInt64 } from '@aztec/stdlib/types';
83
- import { ForkCheckpoint } from '@aztec/world-state';
83
+ import { ForkCheckpoint } from '@aztec/world-state/native';
84
84
 
85
85
  import { DEFAULT_ADDRESS } from '../constants.js';
86
86
  import type { TXEStateMachine } from '../state_machine/index.js';
87
- import type { TXEAccountStore } from '../util/txe_account_store.js';
88
- import { TXEPublicContractDataSource } from '../util/txe_public_contract_data_source.js';
89
87
  import { getSingleTxBlockRequestHash, insertTxEffectIntoWorldTrees, makeTXEBlock } from '../utils/block_creation.js';
88
+ import type { TXEAccountStore } from '../utils/txe_account_store.js';
89
+ import { TXEPublicContractDataSource } from '../utils/txe_public_contract_data_source.js';
90
90
  import type { ITxeExecutionOracle } from './interfaces.js';
91
91
 
92
92
  export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracle {
@@ -116,22 +116,34 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
116
116
  this.logger.debug('Entering Top Level Context');
117
117
  }
118
118
 
119
- utilityAssertCompatibleOracleVersion(version: number): void {
120
- if (version !== ORACLE_VERSION) {
119
+ private contractOracleVersion: { major: number; minor: number } | undefined;
120
+
121
+ assertCompatibleOracleVersion(major: number, minor: number): void {
122
+ if (major !== ORACLE_VERSION_MAJOR) {
123
+ const hint =
124
+ major > ORACLE_VERSION_MAJOR
125
+ ? '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.'
126
+ : '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.';
121
127
  throw new Error(
122
- `Incompatible oracle version. TXE is using version '${ORACLE_VERSION}', but got a request for '${version}'.`,
128
+ `Incompatible aztec cli version: ${hint} See https://docs.aztec.network/errors/8 (expected oracle major version ${ORACLE_VERSION_MAJOR}, got ${major})`,
123
129
  );
124
130
  }
131
+ this.contractOracleVersion = { major, minor };
132
+ }
133
+
134
+ // Prefixed with "nonOracleFunction" as it is not used as an oracle handler.
135
+ nonOracleFunctionGetContractOracleVersion(): { major: number; minor: number } | undefined {
136
+ return this.contractOracleVersion;
125
137
  }
126
138
 
127
139
  // This is typically only invoked in private contexts, but it is convenient to also have it in top-level for testing
128
140
  // setup.
129
- utilityGetRandomField(): Fr {
141
+ getRandomField(): Fr {
130
142
  return Fr.random();
131
143
  }
132
144
 
133
145
  // We instruct users to debug contracts via this oracle, so it makes sense that they'd expect it to also work in tests
134
- utilityLog(level: number, message: string, fields: Fr[]): Promise<void> {
146
+ log(level: number, message: string, fields: Fr[]): Promise<void> {
135
147
  if (!LogLevels[level]) {
136
148
  throw new Error(`Invalid log level: ${level}`);
137
149
  }
@@ -141,25 +153,25 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
141
153
  return Promise.resolve();
142
154
  }
143
155
 
144
- txeGetDefaultAddress(): AztecAddress {
156
+ getDefaultAddress(): AztecAddress {
145
157
  return DEFAULT_ADDRESS;
146
158
  }
147
159
 
148
- async txeGetNextBlockNumber(): Promise<BlockNumber> {
160
+ async getNextBlockNumber(): Promise<BlockNumber> {
149
161
  return BlockNumber((await this.getLastBlockNumber()) + 1);
150
162
  }
151
163
 
152
- txeGetNextBlockTimestamp(): Promise<bigint> {
164
+ getNextBlockTimestamp(): Promise<bigint> {
153
165
  return Promise.resolve(this.nextBlockTimestamp);
154
166
  }
155
167
 
156
- async txeGetLastBlockTimestamp() {
157
- return (await this.stateMachine.node.getBlockHeader('latest'))!.globalVariables.timestamp;
168
+ async getLastBlockTimestamp() {
169
+ return (await this.stateMachine.node.getBlockData('latest'))!.header.globalVariables.timestamp;
158
170
  }
159
171
 
160
- async txeGetLastTxEffects() {
172
+ async getLastTxEffects() {
161
173
  const latestBlockNumber = await this.stateMachine.archiver.getBlockNumber();
162
- const block = await this.stateMachine.archiver.getBlock(latestBlockNumber);
174
+ const block = await this.stateMachine.archiver.getBlock({ number: latestBlockNumber });
163
175
 
164
176
  if (block!.body.txEffects.length != 1) {
165
177
  // Note that calls like env.mine() will result in blocks with no transactions, hitting this
@@ -168,12 +180,22 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
168
180
 
169
181
  const txEffects = block!.body.txEffects[0];
170
182
 
171
- return { txHash: txEffects.txHash, noteHashes: txEffects.noteHashes, nullifiers: txEffects.nullifiers };
183
+ const privateLogs = txEffects.privateLogs;
184
+ if (privateLogs.length > MAX_PRIVATE_LOGS_PER_TX) {
185
+ throw new Error(`${privateLogs.length} private logs exceed max ${MAX_PRIVATE_LOGS_PER_TX}`);
186
+ }
187
+
188
+ return {
189
+ txHash: txEffects.txHash,
190
+ noteHashes: txEffects.noteHashes,
191
+ nullifiers: txEffects.nullifiers,
192
+ privateLogs,
193
+ };
172
194
  }
173
195
 
174
196
  async syncContractNonOracleMethod(contractAddress: AztecAddress, scope: AztecAddress, jobId: string) {
175
197
  if (contractAddress.equals(DEFAULT_ADDRESS)) {
176
- this.logger.debug(`Skipping sync in txeGetPrivateEvents because the events correspond to the default address.`);
198
+ this.logger.debug(`Skipping sync in getPrivateEvents because the events correspond to the default address.`);
177
199
  return;
178
200
  }
179
201
 
@@ -190,7 +212,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
190
212
  );
191
213
  }
192
214
 
193
- async txeGetPrivateEvents(selector: EventSelector, contractAddress: AztecAddress, scope: AztecAddress) {
215
+ async getPrivateEvents(selector: EventSelector, contractAddress: AztecAddress, scope: AztecAddress) {
194
216
  return (
195
217
  await this.privateEventStore.getPrivateEvents(selector, {
196
218
  contractAddress,
@@ -201,7 +223,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
201
223
  ).map(e => e.packedEvent);
202
224
  }
203
225
 
204
- async txeAdvanceBlocksBy(blocks: number) {
226
+ async advanceBlocksBy(blocks: number) {
205
227
  this.logger.debug(`time traveling ${blocks} blocks`);
206
228
 
207
229
  for (let i = 0; i < blocks; i++) {
@@ -209,24 +231,23 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
209
231
  }
210
232
  }
211
233
 
212
- txeAdvanceTimestampBy(duration: UInt64) {
234
+ advanceTimestampBy(duration: UInt64) {
213
235
  this.logger.debug(`time traveling ${duration} seconds`);
214
236
  this.nextBlockTimestamp += duration;
215
237
  }
216
238
 
217
- async txeDeploy(artifact: ContractArtifact, instance: ContractInstanceWithAddress, secret: Fr) {
239
+ private deploymentNullifier(address: AztecAddress): Promise<Fr> {
240
+ return siloNullifier(AztecAddress.fromNumber(CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS), address.toField());
241
+ }
242
+
243
+ async deploy(artifact: ContractArtifact, instance: ContractInstanceWithAddress, secret: Fr) {
218
244
  // Emit deployment nullifier
219
245
  await this.mineBlock({
220
- nullifiers: [
221
- await siloNullifier(
222
- AztecAddress.fromNumber(CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS),
223
- instance.address.toField(),
224
- ),
225
- ],
246
+ nullifiers: [await this.deploymentNullifier(instance.address)],
226
247
  });
227
248
 
228
249
  if (!secret.equals(Fr.ZERO)) {
229
- await this.txeAddAccount(artifact, instance, secret);
250
+ await this.addAccount(artifact, instance, secret);
230
251
  } else {
231
252
  await this.contractStore.addContractInstance(instance);
232
253
  await this.contractStore.addContractArtifact(artifact);
@@ -234,7 +255,16 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
234
255
  }
235
256
  }
236
257
 
237
- async txeAddAccount(artifact: ContractArtifact, instance: ContractInstanceWithAddress, secret: Fr) {
258
+ /**
259
+ * Mines a single block containing only the deployment nullifiers for the contracts at the given addresses.
260
+ */
261
+ async mineDeploymentNullifiers(addresses: AztecAddress[]) {
262
+ await this.mineBlock({
263
+ nullifiers: await Promise.all(addresses.map(address => this.deploymentNullifier(address))),
264
+ });
265
+ }
266
+
267
+ async addAccount(artifact: ContractArtifact, instance: ContractInstanceWithAddress, secret: Fr) {
238
268
  const partialAddress = await computePartialAddress(instance);
239
269
 
240
270
  this.logger.debug(`Deployed ${artifact.name} at ${instance.address}`);
@@ -249,7 +279,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
249
279
  return completeAddress;
250
280
  }
251
281
 
252
- async txeCreateAccount(secret: Fr) {
282
+ async createAccount(secret: Fr) {
253
283
  // This is a foot gun !
254
284
  const completeAddress = await this.keyStore.addAccount(secret, secret);
255
285
  await this.accountStore.setAccount(completeAddress.address, completeAddress);
@@ -259,20 +289,21 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
259
289
  return completeAddress;
260
290
  }
261
291
 
262
- async txeAddAuthWitness(address: AztecAddress, messageHash: Fr) {
292
+ async addAuthWitness(address: AztecAddress, messageHash: Fr) {
263
293
  const account = await this.accountStore.getAccount(address);
264
- const privateKey = await this.keyStore.getMasterSecretKey(account.publicKeys.masterIncomingViewingPublicKey);
294
+ const ivpkMHash = await hashPublicKey(account.publicKeys.ivpkM);
295
+ const privateKey = await this.keyStore.getMasterSecretKey(ivpkMHash);
265
296
 
266
297
  const schnorr = new Schnorr();
267
- const signature = await schnorr.constructSignature(messageHash.toBuffer(), privateKey);
298
+ const signature = await schnorr.constructSignature(messageHash, privateKey);
268
299
 
269
- const authWitness = new AuthWitness(messageHash, [...signature.toBuffer()]);
300
+ const authWitness = new AuthWitness(messageHash, signature.toLimbFields());
270
301
 
271
302
  this.authwits.set(authWitness.requestHash.toString(), authWitness);
272
303
  }
273
304
 
274
305
  async mineBlock(options: { nullifiers?: Fr[] } = {}) {
275
- const blockNumber = await this.txeGetNextBlockNumber();
306
+ const blockNumber = await this.getNextBlockNumber();
276
307
 
277
308
  const txEffect = TxEffect.empty();
278
309
  txEffect.nullifiers = [getSingleTxBlockRequestHash(blockNumber), ...(options.nullifiers ?? [])];
@@ -296,14 +327,17 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
296
327
  await this.stateMachine.handleL2Block(block);
297
328
  }
298
329
 
299
- async txePrivateCallNewFlow(
300
- from: AztecAddress,
330
+ async privateCallNewFlow(
331
+ from: AztecAddress | undefined,
301
332
  targetContractAddress: AztecAddress = AztecAddress.zero(),
302
333
  functionSelector: FunctionSelector = FunctionSelector.empty(),
303
334
  args: Fr[],
304
335
  argsHash: Fr = Fr.zero(),
305
336
  isStaticCall: boolean = false,
337
+ additionalScopes: AztecAddress[] = [],
306
338
  jobId: string,
339
+ authorizedUtilityCallTargets: AztecAddress[],
340
+ gasSettings: GasSettings,
307
341
  ) {
308
342
  this.logger.verbose(
309
343
  `Executing external function ${await this.contractStore.getDebugFunctionName(targetContractAddress, functionSelector)}@${targetContractAddress} isStaticCall=${isStaticCall}`,
@@ -317,12 +351,10 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
317
351
  throw new Error(message);
318
352
  }
319
353
 
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];
354
+ const scopes = from === undefined ? additionalScopes : [from, ...additionalScopes];
323
355
 
324
356
  // Sync notes before executing private function to discover notes from previous transactions
325
- const utilityExecutor = async (call: FunctionCall, execScopes: AccessScopes) => {
357
+ const utilityExecutor = async (call: FunctionCall, execScopes: AztecAddress[]) => {
326
358
  await this.executeUtilityCall(call, execScopes, jobId);
327
359
  };
328
360
 
@@ -333,16 +365,13 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
333
365
  utilityExecutor,
334
366
  blockHeader,
335
367
  jobId,
336
- effectiveScopes,
368
+ scopes,
337
369
  );
338
370
 
339
- const blockNumber = await this.txeGetNextBlockNumber();
371
+ const blockNumber = await this.getNextBlockNumber();
340
372
 
341
- const callContext = new CallContext(from, targetContractAddress, functionSelector, isStaticCall);
342
-
343
- const gasLimits = new Gas(DEFAULT_DA_GAS_LIMIT, DEFAULT_L2_GAS_LIMIT);
344
- const teardownGasLimits = new Gas(DEFAULT_TEARDOWN_DA_GAS_LIMIT, DEFAULT_TEARDOWN_L2_GAS_LIMIT);
345
- const gasSettings = new GasSettings(gasLimits, teardownGasLimits, GasFees.empty(), GasFees.empty());
373
+ const msgSender = from ?? AztecAddress.NULL_MSG_SENDER;
374
+ const callContext = new CallContext(msgSender, targetContractAddress, functionSelector, isStaticCall);
346
375
 
347
376
  const txContext = new TxContext(this.chainId, this.version, gasSettings);
348
377
 
@@ -376,17 +405,25 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
376
405
  senderTaggingStore: this.senderTaggingStore,
377
406
  recipientTaggingStore: this.recipientTaggingStore,
378
407
  senderAddressBookStore: this.senderAddressBookStore,
379
- capsuleStore: this.capsuleStore,
408
+ capsuleService: new CapsuleService(this.capsuleStore, scopes),
380
409
  privateEventStore: this.privateEventStore,
381
410
  contractSyncService: this.stateMachine.contractSyncService,
382
411
  jobId,
383
412
  totalPublicCalldataCount: 0,
384
413
  sideEffectCounter: minRevertibleSideEffectCounter,
385
- scopes: effectiveScopes,
414
+ scopes,
386
415
  // In TXE, the typical transaction entrypoint is skipped, so we need to simulate the actions that such a
387
416
  // contract would perform, including setting senderForTags.
388
417
  senderForTags: from,
389
418
  simulator,
419
+ messageContextService: this.stateMachine.messageContextService,
420
+ l2TipsStore: this.stateMachine.l2TipsProvider,
421
+ hooks: composeHooks({
422
+ authorizeUtilityCall: this.buildAuthorizeUtilityCallHook(
423
+ isStaticCall ? 'private view' : 'private',
424
+ authorizedUtilityCallTargets,
425
+ ),
426
+ }),
390
427
  });
391
428
 
392
429
  // 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.
@@ -409,7 +446,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
409
446
  );
410
447
  const publicFunctionsCalldata = await Promise.all(
411
448
  publicCallRequests.map(async r => {
412
- const calldata = await privateExecutionOracle.privateLoadFromExecutionCache(r.calldataHash);
449
+ const calldata = await privateExecutionOracle.getHashPreimage(r.calldataHash);
413
450
  return new HashedValues(calldata, r.calldataHash);
414
451
  }),
415
452
  );
@@ -494,11 +531,17 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
494
531
  }
495
532
  }
496
533
 
534
+ // Walk the nested private-call tree and collect every offchain effect the transaction emitted.
535
+ // PXE stores these on each `PrivateCallExecutionResult` and they never reach TXE via the
536
+ // `aztec_utl_emitOffchainEffect` foreign-call path (that path only fires at the top-level), so
537
+ // we pull them out here and the RPC wrapper will hand them to `TXESession` for buffering.
538
+ const offchainEffects = collectNested([executionResult], r => r.offchainEffects.map(e => e.data));
539
+
497
540
  if (isStaticCall) {
498
541
  await checkpoint!.revert();
499
542
 
500
543
  await forkedWorldTrees.close();
501
- return executionResult.returnValues ?? [];
544
+ return { returnValues: executionResult.returnValues ?? [], offchainEffects };
502
545
  }
503
546
 
504
547
  const txEffect = TxEffect.empty();
@@ -520,26 +563,21 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
520
563
 
521
564
  await forkedWorldTrees.close();
522
565
 
523
- return executionResult.returnValues ?? [];
566
+ return { returnValues: executionResult.returnValues ?? [], offchainEffects };
524
567
  }
525
568
 
526
- async txePublicCallNewFlow(
527
- from: AztecAddress,
569
+ async publicCallNewFlow(
570
+ from: AztecAddress | undefined,
528
571
  targetContractAddress: AztecAddress,
529
572
  calldata: Fr[],
530
573
  isStaticCall: boolean,
574
+ gasSettings: GasSettings,
531
575
  ) {
532
576
  this.logger.verbose(
533
577
  `Executing public function ${await this.contractStore.getDebugFunctionName(targetContractAddress, FunctionSelector.fromField(calldata[0]))}@${targetContractAddress} isStaticCall=${isStaticCall}`,
534
578
  );
535
579
 
536
- const blockNumber = await this.txeGetNextBlockNumber();
537
-
538
- const gasLimits = new Gas(DEFAULT_DA_GAS_LIMIT, DEFAULT_L2_GAS_LIMIT);
539
-
540
- const teardownGasLimits = new Gas(DEFAULT_TEARDOWN_DA_GAS_LIMIT, DEFAULT_TEARDOWN_L2_GAS_LIMIT);
541
-
542
- const gasSettings = new GasSettings(gasLimits, teardownGasLimits, GasFees.empty(), GasFees.empty());
580
+ const blockNumber = await this.getNextBlockNumber();
543
581
 
544
582
  const txContext = new TxContext(this.chainId, this.version, gasSettings);
545
583
 
@@ -592,7 +630,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
592
630
  // may require producing reverts.
593
631
  const revertibleAccumulatedData = PrivateToPublicAccumulatedData.empty();
594
632
  revertibleAccumulatedData.publicCallRequests[0] = new PublicCallRequest(
595
- from,
633
+ from ?? AztecAddress.NULL_MSG_SENDER,
596
634
  targetContractAddress,
597
635
  isStaticCall,
598
636
  calldataHash,
@@ -678,11 +716,12 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
678
716
  return returnValues ?? [];
679
717
  }
680
718
 
681
- async txeExecuteUtilityFunction(
719
+ async executeUtilityFunction(
682
720
  targetContractAddress: AztecAddress,
683
721
  functionSelector: FunctionSelector,
684
722
  args: Fr[],
685
723
  jobId: string,
724
+ authorizedUtilityCallTargets: AztecAddress[],
686
725
  ) {
687
726
  const artifact = await this.contractStore.getFunctionArtifact(targetContractAddress, functionSelector);
688
727
  if (!artifact) {
@@ -699,7 +738,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
699
738
  },
700
739
  blockHeader,
701
740
  jobId,
702
- 'ALL_SCOPES',
741
+ await this.keyStore.getAccounts(),
703
742
  );
704
743
 
705
744
  const call = FunctionCall.from({
@@ -713,10 +752,15 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
713
752
  returnTypes: [],
714
753
  });
715
754
 
716
- return this.executeUtilityCall(call, 'ALL_SCOPES', jobId);
755
+ return this.executeUtilityCall(call, await this.keyStore.getAccounts(), jobId, authorizedUtilityCallTargets);
717
756
  }
718
757
 
719
- private async executeUtilityCall(call: FunctionCall, scopes: AccessScopes, jobId: string): Promise<Fr[]> {
758
+ private async executeUtilityCall(
759
+ call: FunctionCall,
760
+ scopes: AztecAddress[],
761
+ jobId: string,
762
+ authorizedUtilityCallTargets: AztecAddress[] = [],
763
+ ): Promise<Fr[]> {
720
764
  const entryPointArtifact = await this.contractStore.getFunctionArtifactWithDebugMetadata(call.to, call.selector);
721
765
  if (entryPointArtifact.functionType !== FunctionType.UTILITY) {
722
766
  throw new Error(`Cannot run ${entryPointArtifact.functionType} function as utility`);
@@ -729,6 +773,10 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
729
773
 
730
774
  try {
731
775
  const anchorBlockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
776
+ const simulator = new WASMSimulator();
777
+ const utilityExecutor = async (syncCall: FunctionCall, execScopes: AztecAddress[]) => {
778
+ await this.executeUtilityCall(syncCall, execScopes, jobId);
779
+ };
732
780
  const oracle = new UtilityExecutionOracle({
733
781
  contractAddress: call.to,
734
782
  authWitnesses: [],
@@ -741,12 +789,20 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
741
789
  aztecNode: this.stateMachine.node,
742
790
  recipientTaggingStore: this.recipientTaggingStore,
743
791
  senderAddressBookStore: this.senderAddressBookStore,
744
- capsuleStore: this.capsuleStore,
792
+ capsuleService: new CapsuleService(this.capsuleStore, scopes),
745
793
  privateEventStore: this.privateEventStore,
794
+ messageContextService: this.stateMachine.messageContextService,
795
+ contractSyncService: this.stateMachine.contractSyncService,
796
+ l2TipsStore: this.stateMachine.l2TipsProvider,
746
797
  jobId,
747
798
  scopes,
799
+ simulator,
800
+ hooks: composeHooks({
801
+ authorizeUtilityCall: this.buildAuthorizeUtilityCallHook('utility', authorizedUtilityCallTargets),
802
+ }),
803
+ utilityExecutor,
748
804
  });
749
- const acirExecutionResult = await new WASMSimulator()
805
+ const acirExecutionResult = await simulator
750
806
  .executeUserCircuit(toACVMWitness(0, call.args), entryPointArtifact, new Oracle(oracle).toACIRCallback())
751
807
  .catch((err: Error) => {
752
808
  err.message = resolveAssertionMessageFromError(err, entryPointArtifact);
@@ -774,7 +830,20 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
774
830
  }
775
831
 
776
832
  private async getLastBlockNumber(): Promise<BlockNumber> {
777
- const header = await this.stateMachine.node.getBlockHeader('latest');
778
- return header ? header.globalVariables.blockNumber : BlockNumber.ZERO;
833
+ const block = await this.stateMachine.node.getBlock('latest');
834
+ return block ? block.header.globalVariables.blockNumber : BlockNumber.ZERO;
835
+ }
836
+
837
+ private buildAuthorizeUtilityCallHook(
838
+ callerContext: 'private' | 'private view' | 'utility',
839
+ authorizedTargets: AztecAddress[],
840
+ ): ExecutionHooks['authorizeUtilityCall'] | undefined {
841
+ if (authorizedTargets.length === 0) {
842
+ return undefined;
843
+ }
844
+ return req =>
845
+ Promise.resolve({
846
+ authorized: req.callerContext === callerContext && authorizedTargets.some(t => t.equals(req.target)),
847
+ });
779
848
  }
780
849
  }
@@ -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
+ }