@aztec/txe 0.0.1-commit.7b97ef96e → 0.0.1-commit.7cbc774

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 +40 -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 +38 -22
  41. package/dest/oracle/txe_oracle_top_level_context.d.ts.map +1 -1
  42. package/dest/oracle/txe_oracle_top_level_context.js +141 -72
  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 +574 -238
  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 +14 -8
  58. package/dest/state_machine/dummy_p2p_client.d.ts.map +1 -1
  59. package/dest/state_machine/dummy_p2p_client.js +17 -5
  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 +69 -6
  76. package/dest/txe_session.d.ts.map +1 -1
  77. package/dest/txe_session.js +170 -35
  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 +48 -34
  101. package/src/oracle/txe_oracle_public_context.ts +42 -12
  102. package/src/oracle/txe_oracle_top_level_context.ts +176 -83
  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 +654 -319
  106. package/src/state_machine/archiver.ts +33 -18
  107. package/src/state_machine/dummy_p2p_client.ts +32 -12
  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 +269 -37
  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 {
@@ -107,7 +107,6 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
107
107
  private senderAddressBookStore: SenderAddressBookStore,
108
108
  private capsuleStore: CapsuleStore,
109
109
  private privateEventStore: PrivateEventStore,
110
- private jobId: string,
111
110
  private nextBlockTimestamp: bigint,
112
111
  private version: Fr,
113
112
  private chainId: Fr,
@@ -117,22 +116,34 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
117
116
  this.logger.debug('Entering Top Level Context');
118
117
  }
119
118
 
120
- utilityAssertCompatibleOracleVersion(version: number): void {
121
- 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.';
122
127
  throw new Error(
123
- `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})`,
124
129
  );
125
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;
126
137
  }
127
138
 
128
139
  // This is typically only invoked in private contexts, but it is convenient to also have it in top-level for testing
129
140
  // setup.
130
- utilityGetRandomField(): Fr {
141
+ getRandomField(): Fr {
131
142
  return Fr.random();
132
143
  }
133
144
 
134
145
  // We instruct users to debug contracts via this oracle, so it makes sense that they'd expect it to also work in tests
135
- utilityLog(level: number, message: string, fields: Fr[]): Promise<void> {
146
+ log(level: number, message: string, fields: Fr[]): Promise<void> {
136
147
  if (!LogLevels[level]) {
137
148
  throw new Error(`Invalid log level: ${level}`);
138
149
  }
@@ -142,25 +153,25 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
142
153
  return Promise.resolve();
143
154
  }
144
155
 
145
- txeGetDefaultAddress(): AztecAddress {
156
+ getDefaultAddress(): AztecAddress {
146
157
  return DEFAULT_ADDRESS;
147
158
  }
148
159
 
149
- async txeGetNextBlockNumber(): Promise<BlockNumber> {
160
+ async getNextBlockNumber(): Promise<BlockNumber> {
150
161
  return BlockNumber((await this.getLastBlockNumber()) + 1);
151
162
  }
152
163
 
153
- txeGetNextBlockTimestamp(): Promise<bigint> {
164
+ getNextBlockTimestamp(): Promise<bigint> {
154
165
  return Promise.resolve(this.nextBlockTimestamp);
155
166
  }
156
167
 
157
- async txeGetLastBlockTimestamp() {
158
- return (await this.stateMachine.node.getBlockHeader('latest'))!.globalVariables.timestamp;
168
+ async getLastBlockTimestamp() {
169
+ return (await this.stateMachine.node.getBlockData('latest'))!.header.globalVariables.timestamp;
159
170
  }
160
171
 
161
- async txeGetLastTxEffects() {
172
+ async getLastTxEffects() {
162
173
  const latestBlockNumber = await this.stateMachine.archiver.getBlockNumber();
163
- const block = await this.stateMachine.archiver.getBlock(latestBlockNumber);
174
+ const block = await this.stateMachine.archiver.getBlock({ number: latestBlockNumber });
164
175
 
165
176
  if (block!.body.txEffects.length != 1) {
166
177
  // Note that calls like env.mine() will result in blocks with no transactions, hitting this
@@ -169,10 +180,39 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
169
180
 
170
181
  const txEffects = block!.body.txEffects[0];
171
182
 
172
- 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
+ };
173
194
  }
174
195
 
175
- async txeGetPrivateEvents(selector: EventSelector, contractAddress: AztecAddress, scope: AztecAddress) {
196
+ async syncContractNonOracleMethod(contractAddress: AztecAddress, scope: AztecAddress, jobId: string) {
197
+ if (contractAddress.equals(DEFAULT_ADDRESS)) {
198
+ this.logger.debug(`Skipping sync in getPrivateEvents because the events correspond to the default address.`);
199
+ return;
200
+ }
201
+
202
+ const blockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
203
+ await this.stateMachine.contractSyncService.ensureContractSynced(
204
+ contractAddress,
205
+ null,
206
+ async (call, execScopes) => {
207
+ await this.executeUtilityCall(call, execScopes, jobId);
208
+ },
209
+ blockHeader,
210
+ jobId,
211
+ [scope],
212
+ );
213
+ }
214
+
215
+ async getPrivateEvents(selector: EventSelector, contractAddress: AztecAddress, scope: AztecAddress) {
176
216
  return (
177
217
  await this.privateEventStore.getPrivateEvents(selector, {
178
218
  contractAddress,
@@ -183,7 +223,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
183
223
  ).map(e => e.packedEvent);
184
224
  }
185
225
 
186
- async txeAdvanceBlocksBy(blocks: number) {
226
+ async advanceBlocksBy(blocks: number) {
187
227
  this.logger.debug(`time traveling ${blocks} blocks`);
188
228
 
189
229
  for (let i = 0; i < blocks; i++) {
@@ -191,24 +231,23 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
191
231
  }
192
232
  }
193
233
 
194
- txeAdvanceTimestampBy(duration: UInt64) {
234
+ advanceTimestampBy(duration: UInt64) {
195
235
  this.logger.debug(`time traveling ${duration} seconds`);
196
236
  this.nextBlockTimestamp += duration;
197
237
  }
198
238
 
199
- 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) {
200
244
  // Emit deployment nullifier
201
245
  await this.mineBlock({
202
- nullifiers: [
203
- await siloNullifier(
204
- AztecAddress.fromNumber(CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS),
205
- instance.address.toField(),
206
- ),
207
- ],
246
+ nullifiers: [await this.deploymentNullifier(instance.address)],
208
247
  });
209
248
 
210
249
  if (!secret.equals(Fr.ZERO)) {
211
- await this.txeAddAccount(artifact, instance, secret);
250
+ await this.addAccount(artifact, instance, secret);
212
251
  } else {
213
252
  await this.contractStore.addContractInstance(instance);
214
253
  await this.contractStore.addContractArtifact(artifact);
@@ -216,7 +255,16 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
216
255
  }
217
256
  }
218
257
 
219
- 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) {
220
268
  const partialAddress = await computePartialAddress(instance);
221
269
 
222
270
  this.logger.debug(`Deployed ${artifact.name} at ${instance.address}`);
@@ -231,7 +279,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
231
279
  return completeAddress;
232
280
  }
233
281
 
234
- async txeCreateAccount(secret: Fr) {
282
+ async createAccount(secret: Fr) {
235
283
  // This is a foot gun !
236
284
  const completeAddress = await this.keyStore.addAccount(secret, secret);
237
285
  await this.accountStore.setAccount(completeAddress.address, completeAddress);
@@ -241,20 +289,21 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
241
289
  return completeAddress;
242
290
  }
243
291
 
244
- async txeAddAuthWitness(address: AztecAddress, messageHash: Fr) {
292
+ async addAuthWitness(address: AztecAddress, messageHash: Fr) {
245
293
  const account = await this.accountStore.getAccount(address);
246
- 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);
247
296
 
248
297
  const schnorr = new Schnorr();
249
- const signature = await schnorr.constructSignature(messageHash.toBuffer(), privateKey);
298
+ const signature = await schnorr.constructSignature(messageHash, privateKey);
250
299
 
251
- const authWitness = new AuthWitness(messageHash, [...signature.toBuffer()]);
300
+ const authWitness = new AuthWitness(messageHash, signature.toLimbFields());
252
301
 
253
302
  this.authwits.set(authWitness.requestHash.toString(), authWitness);
254
303
  }
255
304
 
256
305
  async mineBlock(options: { nullifiers?: Fr[] } = {}) {
257
- const blockNumber = await this.txeGetNextBlockNumber();
306
+ const blockNumber = await this.getNextBlockNumber();
258
307
 
259
308
  const txEffect = TxEffect.empty();
260
309
  txEffect.nullifiers = [getSingleTxBlockRequestHash(blockNumber), ...(options.nullifiers ?? [])];
@@ -278,13 +327,17 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
278
327
  await this.stateMachine.handleL2Block(block);
279
328
  }
280
329
 
281
- async txePrivateCallNewFlow(
282
- from: AztecAddress,
330
+ async privateCallNewFlow(
331
+ from: AztecAddress | undefined,
283
332
  targetContractAddress: AztecAddress = AztecAddress.zero(),
284
333
  functionSelector: FunctionSelector = FunctionSelector.empty(),
285
334
  args: Fr[],
286
335
  argsHash: Fr = Fr.zero(),
287
336
  isStaticCall: boolean = false,
337
+ additionalScopes: AztecAddress[] = [],
338
+ jobId: string,
339
+ authorizedUtilityCallTargets: AztecAddress[],
340
+ gasSettings: GasSettings,
288
341
  ) {
289
342
  this.logger.verbose(
290
343
  `Executing external function ${await this.contractStore.getDebugFunctionName(targetContractAddress, functionSelector)}@${targetContractAddress} isStaticCall=${isStaticCall}`,
@@ -298,13 +351,11 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
298
351
  throw new Error(message);
299
352
  }
300
353
 
301
- // When `from` is the zero address (e.g. when deploying a new account contract), we return an
302
- // empty scope list which acts as deny-all: no notes are visible and no keys are accessible.
303
- const effectiveScopes = from.isZero() ? [] : [from];
354
+ const scopes = from === undefined ? additionalScopes : [from, ...additionalScopes];
304
355
 
305
356
  // Sync notes before executing private function to discover notes from previous transactions
306
- const utilityExecutor = async (call: FunctionCall, execScopes: AccessScopes) => {
307
- await this.executeUtilityCall(call, execScopes);
357
+ const utilityExecutor = async (call: FunctionCall, execScopes: AztecAddress[]) => {
358
+ await this.executeUtilityCall(call, execScopes, jobId);
308
359
  };
309
360
 
310
361
  const blockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
@@ -313,17 +364,14 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
313
364
  functionSelector,
314
365
  utilityExecutor,
315
366
  blockHeader,
316
- this.jobId,
317
- effectiveScopes,
367
+ jobId,
368
+ scopes,
318
369
  );
319
370
 
320
- const blockNumber = await this.txeGetNextBlockNumber();
321
-
322
- const callContext = new CallContext(from, targetContractAddress, functionSelector, isStaticCall);
371
+ const blockNumber = await this.getNextBlockNumber();
323
372
 
324
- const gasLimits = new Gas(DEFAULT_DA_GAS_LIMIT, DEFAULT_L2_GAS_LIMIT);
325
- const teardownGasLimits = new Gas(DEFAULT_TEARDOWN_DA_GAS_LIMIT, DEFAULT_TEARDOWN_L2_GAS_LIMIT);
326
- 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);
327
375
 
328
376
  const txContext = new TxContext(this.chainId, this.version, gasSettings);
329
377
 
@@ -357,17 +405,25 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
357
405
  senderTaggingStore: this.senderTaggingStore,
358
406
  recipientTaggingStore: this.recipientTaggingStore,
359
407
  senderAddressBookStore: this.senderAddressBookStore,
360
- capsuleStore: this.capsuleStore,
408
+ capsuleService: new CapsuleService(this.capsuleStore, scopes),
361
409
  privateEventStore: this.privateEventStore,
362
410
  contractSyncService: this.stateMachine.contractSyncService,
363
- jobId: this.jobId,
411
+ jobId,
364
412
  totalPublicCalldataCount: 0,
365
413
  sideEffectCounter: minRevertibleSideEffectCounter,
366
- scopes: effectiveScopes,
414
+ scopes,
367
415
  // In TXE, the typical transaction entrypoint is skipped, so we need to simulate the actions that such a
368
416
  // contract would perform, including setting senderForTags.
369
417
  senderForTags: from,
370
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
+ }),
371
427
  });
372
428
 
373
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.
@@ -390,7 +446,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
390
446
  );
391
447
  const publicFunctionsCalldata = await Promise.all(
392
448
  publicCallRequests.map(async r => {
393
- const calldata = await privateExecutionOracle.privateLoadFromExecutionCache(r.calldataHash);
449
+ const calldata = await privateExecutionOracle.getHashPreimage(r.calldataHash);
394
450
  return new HashedValues(calldata, r.calldataHash);
395
451
  }),
396
452
  );
@@ -475,11 +531,17 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
475
531
  }
476
532
  }
477
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
+
478
540
  if (isStaticCall) {
479
541
  await checkpoint!.revert();
480
542
 
481
543
  await forkedWorldTrees.close();
482
- return executionResult.returnValues ?? [];
544
+ return { returnValues: executionResult.returnValues ?? [], offchainEffects };
483
545
  }
484
546
 
485
547
  const txEffect = TxEffect.empty();
@@ -501,26 +563,21 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
501
563
 
502
564
  await forkedWorldTrees.close();
503
565
 
504
- return executionResult.returnValues ?? [];
566
+ return { returnValues: executionResult.returnValues ?? [], offchainEffects };
505
567
  }
506
568
 
507
- async txePublicCallNewFlow(
508
- from: AztecAddress,
569
+ async publicCallNewFlow(
570
+ from: AztecAddress | undefined,
509
571
  targetContractAddress: AztecAddress,
510
572
  calldata: Fr[],
511
573
  isStaticCall: boolean,
574
+ gasSettings: GasSettings,
512
575
  ) {
513
576
  this.logger.verbose(
514
577
  `Executing public function ${await this.contractStore.getDebugFunctionName(targetContractAddress, FunctionSelector.fromField(calldata[0]))}@${targetContractAddress} isStaticCall=${isStaticCall}`,
515
578
  );
516
579
 
517
- const blockNumber = await this.txeGetNextBlockNumber();
518
-
519
- const gasLimits = new Gas(DEFAULT_DA_GAS_LIMIT, DEFAULT_L2_GAS_LIMIT);
520
-
521
- const teardownGasLimits = new Gas(DEFAULT_TEARDOWN_DA_GAS_LIMIT, DEFAULT_TEARDOWN_L2_GAS_LIMIT);
522
-
523
- const gasSettings = new GasSettings(gasLimits, teardownGasLimits, GasFees.empty(), GasFees.empty());
580
+ const blockNumber = await this.getNextBlockNumber();
524
581
 
525
582
  const txContext = new TxContext(this.chainId, this.version, gasSettings);
526
583
 
@@ -573,7 +630,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
573
630
  // may require producing reverts.
574
631
  const revertibleAccumulatedData = PrivateToPublicAccumulatedData.empty();
575
632
  revertibleAccumulatedData.publicCallRequests[0] = new PublicCallRequest(
576
- from,
633
+ from ?? AztecAddress.NULL_MSG_SENDER,
577
634
  targetContractAddress,
578
635
  isStaticCall,
579
636
  calldataHash,
@@ -659,7 +716,13 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
659
716
  return returnValues ?? [];
660
717
  }
661
718
 
662
- async txeExecuteUtilityFunction(targetContractAddress: AztecAddress, functionSelector: FunctionSelector, args: Fr[]) {
719
+ async executeUtilityFunction(
720
+ targetContractAddress: AztecAddress,
721
+ functionSelector: FunctionSelector,
722
+ args: Fr[],
723
+ jobId: string,
724
+ authorizedUtilityCallTargets: AztecAddress[],
725
+ ) {
663
726
  const artifact = await this.contractStore.getFunctionArtifact(targetContractAddress, functionSelector);
664
727
  if (!artifact) {
665
728
  throw new Error(`Cannot call ${functionSelector} as there is no artifact found at ${targetContractAddress}.`);
@@ -671,11 +734,11 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
671
734
  targetContractAddress,
672
735
  functionSelector,
673
736
  async (call, execScopes) => {
674
- await this.executeUtilityCall(call, execScopes);
737
+ await this.executeUtilityCall(call, execScopes, jobId);
675
738
  },
676
739
  blockHeader,
677
- this.jobId,
678
- 'ALL_SCOPES',
740
+ jobId,
741
+ await this.keyStore.getAccounts(),
679
742
  );
680
743
 
681
744
  const call = FunctionCall.from({
@@ -689,10 +752,15 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
689
752
  returnTypes: [],
690
753
  });
691
754
 
692
- return this.executeUtilityCall(call, 'ALL_SCOPES');
755
+ return this.executeUtilityCall(call, await this.keyStore.getAccounts(), jobId, authorizedUtilityCallTargets);
693
756
  }
694
757
 
695
- private async executeUtilityCall(call: FunctionCall, scopes: AccessScopes): Promise<Fr[]> {
758
+ private async executeUtilityCall(
759
+ call: FunctionCall,
760
+ scopes: AztecAddress[],
761
+ jobId: string,
762
+ authorizedUtilityCallTargets: AztecAddress[] = [],
763
+ ): Promise<Fr[]> {
696
764
  const entryPointArtifact = await this.contractStore.getFunctionArtifactWithDebugMetadata(call.to, call.selector);
697
765
  if (entryPointArtifact.functionType !== FunctionType.UTILITY) {
698
766
  throw new Error(`Cannot run ${entryPointArtifact.functionType} function as utility`);
@@ -705,6 +773,10 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
705
773
 
706
774
  try {
707
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
+ };
708
780
  const oracle = new UtilityExecutionOracle({
709
781
  contractAddress: call.to,
710
782
  authWitnesses: [],
@@ -717,12 +789,20 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
717
789
  aztecNode: this.stateMachine.node,
718
790
  recipientTaggingStore: this.recipientTaggingStore,
719
791
  senderAddressBookStore: this.senderAddressBookStore,
720
- capsuleStore: this.capsuleStore,
792
+ capsuleService: new CapsuleService(this.capsuleStore, scopes),
721
793
  privateEventStore: this.privateEventStore,
722
- jobId: this.jobId,
794
+ messageContextService: this.stateMachine.messageContextService,
795
+ contractSyncService: this.stateMachine.contractSyncService,
796
+ l2TipsStore: this.stateMachine.l2TipsProvider,
797
+ jobId,
723
798
  scopes,
799
+ simulator,
800
+ hooks: composeHooks({
801
+ authorizeUtilityCall: this.buildAuthorizeUtilityCallHook('utility', authorizedUtilityCallTargets),
802
+ }),
803
+ utilityExecutor,
724
804
  });
725
- const acirExecutionResult = await new WASMSimulator()
805
+ const acirExecutionResult = await simulator
726
806
  .executeUserCircuit(toACVMWitness(0, call.args), entryPointArtifact, new Oracle(oracle).toACIRCallback())
727
807
  .catch((err: Error) => {
728
808
  err.message = resolveAssertionMessageFromError(err, entryPointArtifact);
@@ -750,7 +830,20 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
750
830
  }
751
831
 
752
832
  private async getLastBlockNumber(): Promise<BlockNumber> {
753
- const header = await this.stateMachine.node.getBlockHeader('latest');
754
- 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
+ });
755
848
  }
756
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
+ }