@aztec/txe 0.0.1-commit.b33fc05d0 → 0.0.1-commit.b3d3157a

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 (150) 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-2FGZE6YL.js +265 -0
  20. package/dest/chunk-2FGZE6YL.js.map +7 -0
  21. package/dest/chunk-6HRVNWJU.js +304 -0
  22. package/dest/chunk-6HRVNWJU.js.map +7 -0
  23. package/dest/chunk-BJVAAXNA.js +3 -0
  24. package/dest/chunk-BJVAAXNA.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 +38642 -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 +32 -12
  56. package/dest/oracle/txe_oracle_top_level_context.d.ts.map +1 -1
  57. package/dest/oracle/txe_oracle_top_level_context.js +146 -54
  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 -230
  68. package/dest/rpc_translator.d.ts.map +1 -1
  69. package/dest/rpc_translator.js +708 -616
  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.d.ts +5 -5
  88. package/dest/state_machine/synchronizer.d.ts.map +1 -1
  89. package/dest/state_machine/synchronizer.js +4 -4
  90. package/dest/txe_session.d.ts +74 -12
  91. package/dest/txe_session.d.ts.map +1 -1
  92. package/dest/txe_session.js +171 -36
  93. package/dest/utils/encoding.d.ts +191 -0
  94. package/dest/utils/encoding.d.ts.map +1 -0
  95. package/dest/{util → utils}/encoding.js +7 -2
  96. package/dest/{util → utils}/expected_failure_error.d.ts +1 -1
  97. package/dest/utils/expected_failure_error.d.ts.map +1 -0
  98. package/dest/{util → utils}/txe_account_store.d.ts +1 -1
  99. package/dest/utils/txe_account_store.d.ts.map +1 -0
  100. package/dest/utils/txe_artifact_resolver.d.ts +37 -0
  101. package/dest/utils/txe_artifact_resolver.d.ts.map +1 -0
  102. package/dest/utils/txe_artifact_resolver.js +161 -0
  103. package/dest/utils/txe_public_contract_data_source.d.ts +20 -0
  104. package/dest/utils/txe_public_contract_data_source.d.ts.map +1 -0
  105. package/dest/{util → utils}/txe_public_contract_data_source.js +1 -3
  106. package/dest/worker.bundle.js +3 -0
  107. package/dest/worker.bundle.js.map +7 -0
  108. package/dest/worker.d.ts +2 -0
  109. package/dest/worker.d.ts.map +1 -0
  110. package/dest/worker.js +92 -0
  111. package/package.json +38 -21
  112. package/src/bin/check_txe_oracle_version.ts +70 -0
  113. package/src/bin/index.ts +11 -2
  114. package/src/bin/oracle_test_server.ts +51 -0
  115. package/src/constants.ts +10 -0
  116. package/src/dispatcher_pool.ts +316 -0
  117. package/src/index.ts +97 -227
  118. package/src/msgpackr_fr_extension.ts +23 -0
  119. package/src/oracle/interfaces.ts +29 -7
  120. package/src/oracle/test-resolver/fixtures.ts +84 -0
  121. package/src/oracle/test-resolver/index.ts +45 -0
  122. package/src/oracle/test-resolver/resolver.ts +165 -0
  123. package/src/oracle/txe_oracle_public_context.ts +60 -0
  124. package/src/oracle/txe_oracle_registry.ts +401 -0
  125. package/src/oracle/txe_oracle_top_level_context.ts +177 -61
  126. package/src/oracle/txe_oracle_version.ts +17 -0
  127. package/src/oracle/txe_private_execution_oracle.ts +30 -0
  128. package/src/rpc_server.ts +87 -0
  129. package/src/rpc_translator.ts +789 -903
  130. package/src/state_machine/archiver.ts +38 -16
  131. package/src/state_machine/dummy_p2p_client.ts +31 -11
  132. package/src/state_machine/global_variable_builder.ts +18 -3
  133. package/src/state_machine/index.ts +20 -5
  134. package/src/state_machine/mock_epoch_cache.ts +38 -3
  135. package/src/state_machine/synchronizer.ts +5 -5
  136. package/src/txe_session.ts +281 -38
  137. package/src/{util → utils}/encoding.ts +8 -2
  138. package/src/utils/txe_artifact_resolver.ts +217 -0
  139. package/src/{util → utils}/txe_public_contract_data_source.ts +0 -2
  140. package/src/worker.ts +98 -0
  141. package/dest/util/encoding.d.ts +0 -720
  142. package/dest/util/encoding.d.ts.map +0 -1
  143. package/dest/util/expected_failure_error.d.ts.map +0 -1
  144. package/dest/util/txe_account_store.d.ts.map +0 -1
  145. package/dest/util/txe_public_contract_data_source.d.ts +0 -20
  146. package/dest/util/txe_public_contract_data_source.d.ts.map +0 -1
  147. /package/dest/{util → utils}/expected_failure_error.js +0 -0
  148. /package/dest/{util → utils}/txe_account_store.js +0 -0
  149. /package/src/{util → utils}/expected_failure_error.ts +0 -0
  150. /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,14 @@ 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
- import { DEFAULT_ADDRESS } from '../constants.js';
85
+ import { DEFAULT_ADDRESS, MAX_PRIVATE_EVENTS_PER_TXE_QUERY, MAX_PRIVATE_EVENT_LEN } 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 type { TXEArtifactResolver } from '../utils/txe_artifact_resolver.js';
90
+ import { TXEPublicContractDataSource } from '../utils/txe_public_contract_data_source.js';
90
91
  import type { ITxeExecutionOracle } from './interfaces.js';
91
92
 
92
93
  export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracle {
@@ -111,17 +112,32 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
111
112
  private version: Fr,
112
113
  private chainId: Fr,
113
114
  private authwits: Map<string, AuthWitness>,
115
+ private readonly artifactResolver: TXEArtifactResolver,
116
+ private readonly rootPath: string,
117
+ private readonly packageName: string,
114
118
  ) {
115
119
  this.logger = createLogger('txe:top_level_context');
116
120
  this.logger.debug('Entering Top Level Context');
117
121
  }
118
122
 
119
- assertCompatibleOracleVersion(version: number): void {
120
- if (version !== ORACLE_VERSION) {
123
+ private contractOracleVersion: { major: number; minor: number } | undefined;
124
+
125
+ assertCompatibleOracleVersion(major: number, minor: number): void {
126
+ if (major !== ORACLE_VERSION_MAJOR) {
127
+ const hint =
128
+ major > ORACLE_VERSION_MAJOR
129
+ ? '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.'
130
+ : '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
131
  throw new Error(
122
- `Incompatible oracle version. TXE is using version '${ORACLE_VERSION}', but got a request for '${version}'.`,
132
+ `Incompatible aztec cli version: ${hint} See https://docs.aztec.network/errors/8 (expected oracle major version ${ORACLE_VERSION_MAJOR}, got ${major})`,
123
133
  );
124
134
  }
135
+ this.contractOracleVersion = { major, minor };
136
+ }
137
+
138
+ // Prefixed with "nonOracleFunction" as it is not used as an oracle handler.
139
+ nonOracleFunctionGetContractOracleVersion(): { major: number; minor: number } | undefined {
140
+ return this.contractOracleVersion;
125
141
  }
126
142
 
127
143
  // This is typically only invoked in private contexts, but it is convenient to also have it in top-level for testing
@@ -131,7 +147,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
131
147
  }
132
148
 
133
149
  // We instruct users to debug contracts via this oracle, so it makes sense that they'd expect it to also work in tests
134
- log(level: number, message: string, fields: Fr[]): Promise<void> {
150
+ log(level: number, message: string, _fieldsSize: number, fields: Fr[]): Promise<void> {
135
151
  if (!LogLevels[level]) {
136
152
  throw new Error(`Invalid log level: ${level}`);
137
153
  }
@@ -154,12 +170,12 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
154
170
  }
155
171
 
156
172
  async getLastBlockTimestamp() {
157
- return (await this.stateMachine.node.getBlockHeader('latest'))!.globalVariables.timestamp;
173
+ return (await this.stateMachine.node.getBlockData('latest'))!.header.globalVariables.timestamp;
158
174
  }
159
175
 
160
176
  async getLastTxEffects() {
161
177
  const latestBlockNumber = await this.stateMachine.archiver.getBlockNumber();
162
- const block = await this.stateMachine.archiver.getBlock(latestBlockNumber);
178
+ const block = await this.stateMachine.archiver.getBlock({ number: latestBlockNumber });
163
179
 
164
180
  if (block!.body.txEffects.length != 1) {
165
181
  // Note that calls like env.mine() will result in blocks with no transactions, hitting this
@@ -168,7 +184,17 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
168
184
 
169
185
  const txEffects = block!.body.txEffects[0];
170
186
 
171
- 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
+ };
172
198
  }
173
199
 
174
200
  async syncContractNonOracleMethod(contractAddress: AztecAddress, scope: AztecAddress, jobId: string) {
@@ -191,7 +217,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
191
217
  }
192
218
 
193
219
  async getPrivateEvents(selector: EventSelector, contractAddress: AztecAddress, scope: AztecAddress) {
194
- return (
220
+ const events = (
195
221
  await this.privateEventStore.getPrivateEvents(selector, {
196
222
  contractAddress,
197
223
  scopes: [scope],
@@ -199,6 +225,15 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
199
225
  toBlock: (await this.getLastBlockNumber()) + 1,
200
226
  })
201
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;
202
237
  }
203
238
 
204
239
  async advanceBlocksBy(blocks: number) {
@@ -214,27 +249,71 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
214
249
  this.nextBlockTimestamp += duration;
215
250
  }
216
251
 
217
- 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
+
218
275
  // Emit deployment nullifier
219
276
  await this.mineBlock({
220
- nullifiers: [
221
- await siloNullifier(
222
- AztecAddress.fromNumber(CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS),
223
- instance.address.toField(),
224
- ),
225
- ],
277
+ nullifiers: [await this.deploymentNullifier(instance.address)],
226
278
  });
227
279
 
228
280
  if (!secret.equals(Fr.ZERO)) {
229
- await this.addAccount(artifact, instance, secret);
281
+ await this.registerContractAndAddAccount(artifact, instance, secret);
230
282
  } else {
231
283
  await this.contractStore.addContractInstance(instance);
232
284
  await this.contractStore.addContractArtifact(artifact);
233
285
  this.logger.debug(`Deployed ${artifact.name} at ${instance.address}`);
234
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
+ });
305
+ }
306
+
307
+ async addAccount(secret: Fr) {
308
+ const { artifact, instance } = await this.artifactResolver.resolveAccountArtifact(secret);
309
+ return this.registerContractAndAddAccount(artifact, instance, secret);
235
310
  }
236
311
 
237
- async addAccount(artifact: ContractArtifact, instance: ContractInstanceWithAddress, secret: Fr) {
312
+ private async registerContractAndAddAccount(
313
+ artifact: ContractArtifact,
314
+ instance: ContractInstanceWithAddress,
315
+ secret: Fr,
316
+ ) {
238
317
  const partialAddress = await computePartialAddress(instance);
239
318
 
240
319
  this.logger.debug(`Deployed ${artifact.name} at ${instance.address}`);
@@ -261,12 +340,13 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
261
340
 
262
341
  async addAuthWitness(address: AztecAddress, messageHash: Fr) {
263
342
  const account = await this.accountStore.getAccount(address);
264
- 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);
265
345
 
266
346
  const schnorr = new Schnorr();
267
- const signature = await schnorr.constructSignature(messageHash.toBuffer(), privateKey);
347
+ const signature = await schnorr.constructSignature(messageHash, privateKey);
268
348
 
269
- const authWitness = new AuthWitness(messageHash, [...signature.toBuffer()]);
349
+ const authWitness = new AuthWitness(messageHash, signature.toLimbFields());
270
350
 
271
351
  this.authwits.set(authWitness.requestHash.toString(), authWitness);
272
352
  }
@@ -297,13 +377,16 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
297
377
  }
298
378
 
299
379
  async privateCallNewFlow(
300
- from: AztecAddress,
380
+ from: AztecAddress | undefined,
301
381
  targetContractAddress: AztecAddress = AztecAddress.zero(),
302
382
  functionSelector: FunctionSelector = FunctionSelector.empty(),
303
383
  args: Fr[],
304
384
  argsHash: Fr = Fr.zero(),
305
385
  isStaticCall: boolean = false,
386
+ additionalScopes: AztecAddress[] = [],
306
387
  jobId: string,
388
+ authorizedUtilityCallTargets: AztecAddress[],
389
+ gasSettings: GasSettings,
307
390
  ) {
308
391
  this.logger.verbose(
309
392
  `Executing external function ${await this.contractStore.getDebugFunctionName(targetContractAddress, functionSelector)}@${targetContractAddress} isStaticCall=${isStaticCall}`,
@@ -317,12 +400,10 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
317
400
  throw new Error(message);
318
401
  }
319
402
 
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];
403
+ const scopes = from === undefined ? additionalScopes : [from, ...additionalScopes];
323
404
 
324
405
  // Sync notes before executing private function to discover notes from previous transactions
325
- const utilityExecutor = async (call: FunctionCall, execScopes: AccessScopes) => {
406
+ const utilityExecutor = async (call: FunctionCall, execScopes: AztecAddress[]) => {
326
407
  await this.executeUtilityCall(call, execScopes, jobId);
327
408
  };
328
409
 
@@ -333,16 +414,13 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
333
414
  utilityExecutor,
334
415
  blockHeader,
335
416
  jobId,
336
- effectiveScopes,
417
+ scopes,
337
418
  );
338
419
 
339
420
  const blockNumber = await this.getNextBlockNumber();
340
421
 
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());
422
+ const msgSender = from ?? AztecAddress.NULL_MSG_SENDER;
423
+ const callContext = new CallContext(msgSender, targetContractAddress, functionSelector, isStaticCall);
346
424
 
347
425
  const txContext = new TxContext(this.chainId, this.version, gasSettings);
348
426
 
@@ -376,18 +454,25 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
376
454
  senderTaggingStore: this.senderTaggingStore,
377
455
  recipientTaggingStore: this.recipientTaggingStore,
378
456
  senderAddressBookStore: this.senderAddressBookStore,
379
- capsuleStore: this.capsuleStore,
457
+ capsuleService: new CapsuleService(this.capsuleStore, scopes),
380
458
  privateEventStore: this.privateEventStore,
381
459
  contractSyncService: this.stateMachine.contractSyncService,
382
460
  jobId,
383
461
  totalPublicCalldataCount: 0,
384
462
  sideEffectCounter: minRevertibleSideEffectCounter,
385
- scopes: effectiveScopes,
463
+ scopes,
386
464
  // In TXE, the typical transaction entrypoint is skipped, so we need to simulate the actions that such a
387
465
  // contract would perform, including setting senderForTags.
388
466
  senderForTags: from,
389
467
  simulator,
390
468
  messageContextService: this.stateMachine.messageContextService,
469
+ l2TipsStore: this.stateMachine.l2TipsProvider,
470
+ hooks: composeHooks({
471
+ authorizeUtilityCall: this.buildAuthorizeUtilityCallHook(
472
+ isStaticCall ? 'private view' : 'private',
473
+ authorizedUtilityCallTargets,
474
+ ),
475
+ }),
391
476
  });
392
477
 
393
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.
@@ -410,7 +495,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
410
495
  );
411
496
  const publicFunctionsCalldata = await Promise.all(
412
497
  publicCallRequests.map(async r => {
413
- const calldata = await privateExecutionOracle.loadFromExecutionCache(r.calldataHash);
498
+ const calldata = await privateExecutionOracle.getHashPreimage(r.calldataHash);
414
499
  return new HashedValues(calldata, r.calldataHash);
415
500
  }),
416
501
  );
@@ -495,11 +580,17 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
495
580
  }
496
581
  }
497
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
+
498
589
  if (isStaticCall) {
499
590
  await checkpoint!.revert();
500
591
 
501
592
  await forkedWorldTrees.close();
502
- return executionResult.returnValues ?? [];
593
+ return { returnValues: executionResult.returnValues ?? [], offchainEffects };
503
594
  }
504
595
 
505
596
  const txEffect = TxEffect.empty();
@@ -521,14 +612,15 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
521
612
 
522
613
  await forkedWorldTrees.close();
523
614
 
524
- return executionResult.returnValues ?? [];
615
+ return { returnValues: executionResult.returnValues ?? [], offchainEffects };
525
616
  }
526
617
 
527
618
  async publicCallNewFlow(
528
- from: AztecAddress,
619
+ from: AztecAddress | undefined,
529
620
  targetContractAddress: AztecAddress,
530
621
  calldata: Fr[],
531
622
  isStaticCall: boolean,
623
+ gasSettings: GasSettings,
532
624
  ) {
533
625
  this.logger.verbose(
534
626
  `Executing public function ${await this.contractStore.getDebugFunctionName(targetContractAddress, FunctionSelector.fromField(calldata[0]))}@${targetContractAddress} isStaticCall=${isStaticCall}`,
@@ -536,12 +628,6 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
536
628
 
537
629
  const blockNumber = await this.getNextBlockNumber();
538
630
 
539
- const gasLimits = new Gas(DEFAULT_DA_GAS_LIMIT, DEFAULT_L2_GAS_LIMIT);
540
-
541
- const teardownGasLimits = new Gas(DEFAULT_TEARDOWN_DA_GAS_LIMIT, DEFAULT_TEARDOWN_L2_GAS_LIMIT);
542
-
543
- const gasSettings = new GasSettings(gasLimits, teardownGasLimits, GasFees.empty(), GasFees.empty());
544
-
545
631
  const txContext = new TxContext(this.chainId, this.version, gasSettings);
546
632
 
547
633
  const anchorBlockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
@@ -593,7 +679,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
593
679
  // may require producing reverts.
594
680
  const revertibleAccumulatedData = PrivateToPublicAccumulatedData.empty();
595
681
  revertibleAccumulatedData.publicCallRequests[0] = new PublicCallRequest(
596
- from,
682
+ from ?? AztecAddress.NULL_MSG_SENDER,
597
683
  targetContractAddress,
598
684
  isStaticCall,
599
685
  calldataHash,
@@ -684,6 +770,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
684
770
  functionSelector: FunctionSelector,
685
771
  args: Fr[],
686
772
  jobId: string,
773
+ authorizedUtilityCallTargets: AztecAddress[],
687
774
  ) {
688
775
  const artifact = await this.contractStore.getFunctionArtifact(targetContractAddress, functionSelector);
689
776
  if (!artifact) {
@@ -700,7 +787,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
700
787
  },
701
788
  blockHeader,
702
789
  jobId,
703
- 'ALL_SCOPES',
790
+ await this.keyStore.getAccounts(),
704
791
  );
705
792
 
706
793
  const call = FunctionCall.from({
@@ -714,10 +801,15 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
714
801
  returnTypes: [],
715
802
  });
716
803
 
717
- return this.executeUtilityCall(call, 'ALL_SCOPES', jobId);
804
+ return this.executeUtilityCall(call, await this.keyStore.getAccounts(), jobId, authorizedUtilityCallTargets);
718
805
  }
719
806
 
720
- private async executeUtilityCall(call: FunctionCall, scopes: AccessScopes, jobId: string): Promise<Fr[]> {
807
+ private async executeUtilityCall(
808
+ call: FunctionCall,
809
+ scopes: AztecAddress[],
810
+ jobId: string,
811
+ authorizedUtilityCallTargets: AztecAddress[] = [],
812
+ ): Promise<Fr[]> {
721
813
  const entryPointArtifact = await this.contractStore.getFunctionArtifactWithDebugMetadata(call.to, call.selector);
722
814
  if (entryPointArtifact.functionType !== FunctionType.UTILITY) {
723
815
  throw new Error(`Cannot run ${entryPointArtifact.functionType} function as utility`);
@@ -730,6 +822,10 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
730
822
 
731
823
  try {
732
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
+ };
733
829
  const oracle = new UtilityExecutionOracle({
734
830
  contractAddress: call.to,
735
831
  authWitnesses: [],
@@ -742,13 +838,20 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
742
838
  aztecNode: this.stateMachine.node,
743
839
  recipientTaggingStore: this.recipientTaggingStore,
744
840
  senderAddressBookStore: this.senderAddressBookStore,
745
- capsuleStore: this.capsuleStore,
841
+ capsuleService: new CapsuleService(this.capsuleStore, scopes),
746
842
  privateEventStore: this.privateEventStore,
747
843
  messageContextService: this.stateMachine.messageContextService,
844
+ contractSyncService: this.stateMachine.contractSyncService,
845
+ l2TipsStore: this.stateMachine.l2TipsProvider,
748
846
  jobId,
749
847
  scopes,
848
+ simulator,
849
+ hooks: composeHooks({
850
+ authorizeUtilityCall: this.buildAuthorizeUtilityCallHook('utility', authorizedUtilityCallTargets),
851
+ }),
852
+ utilityExecutor,
750
853
  });
751
- const acirExecutionResult = await new WASMSimulator()
854
+ const acirExecutionResult = await simulator
752
855
  .executeUserCircuit(toACVMWitness(0, call.args), entryPointArtifact, new Oracle(oracle).toACIRCallback())
753
856
  .catch((err: Error) => {
754
857
  err.message = resolveAssertionMessageFromError(err, entryPointArtifact);
@@ -776,7 +879,20 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
776
879
  }
777
880
 
778
881
  private async getLastBlockNumber(): Promise<BlockNumber> {
779
- const header = await this.stateMachine.node.getBlockHeader('latest');
780
- 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
+ });
781
897
  }
782
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
+ }