@aztec/txe 5.0.0-private.20260318 → 5.0.0-rc.1

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 (154) hide show
  1. package/dest/AuthRegistry-CPGFQR26.js +3 -0
  2. package/dest/AuthRegistry-CPGFQR26.js.map +7 -0
  3. package/dest/ContractClassRegistry-EHVIHGEK.js +3 -0
  4. package/dest/ContractClassRegistry-EHVIHGEK.js.map +7 -0
  5. package/dest/ContractInstanceRegistry-DWZDXHRG.js +3 -0
  6. package/dest/ContractInstanceRegistry-DWZDXHRG.js.map +7 -0
  7. package/dest/FeeJuice-MI32ZO7B.js +3 -0
  8. package/dest/FeeJuice-MI32ZO7B.js.map +7 -0
  9. package/dest/HandshakeRegistry-3KSP3ITH.js +3 -0
  10. package/dest/HandshakeRegistry-3KSP3ITH.js.map +7 -0
  11. package/dest/MultiCallEntrypoint-IU7HYFYE.js +3 -0
  12. package/dest/MultiCallEntrypoint-IU7HYFYE.js.map +7 -0
  13. package/dest/SchnorrAccount-6TUE7JX4.js +3 -0
  14. package/dest/SchnorrAccount-6TUE7JX4.js.map +7 -0
  15. package/dest/SchnorrInitializerlessAccount-S3DU2DJK.js +3 -0
  16. package/dest/SchnorrInitializerlessAccount-S3DU2DJK.js.map +7 -0
  17. package/dest/bin/check_txe_oracle_version.d.ts +2 -0
  18. package/dest/bin/check_txe_oracle_version.d.ts.map +1 -0
  19. package/dest/bin/check_txe_oracle_version.js +61 -0
  20. package/dest/bin/index.js +3 -30
  21. package/dest/bin/index.js.map +7 -0
  22. package/dest/bin/oracle_test_server.d.ts +3 -0
  23. package/dest/bin/oracle_test_server.d.ts.map +1 -0
  24. package/dest/bin/oracle_test_server.js +41 -0
  25. package/dest/chunk-5U25VAFR.js +265 -0
  26. package/dest/chunk-5U25VAFR.js.map +7 -0
  27. package/dest/chunk-BJVAAXNA.js +3 -0
  28. package/dest/chunk-BJVAAXNA.js.map +7 -0
  29. package/dest/chunk-UPW55EJX.js +304 -0
  30. package/dest/chunk-UPW55EJX.js.map +7 -0
  31. package/dest/constants.d.ts +5 -1
  32. package/dest/constants.d.ts.map +1 -1
  33. package/dest/constants.js +8 -0
  34. package/dest/dispatcher_pool.d.ts +67 -0
  35. package/dest/dispatcher_pool.d.ts.map +1 -0
  36. package/dest/dispatcher_pool.js +286 -0
  37. package/dest/index.d.ts +51 -7
  38. package/dest/index.d.ts.map +1 -1
  39. package/dest/index.js +70 -190
  40. package/dest/metafile.json +38829 -0
  41. package/dest/msgpackr_fr_extension.d.ts +2 -0
  42. package/dest/msgpackr_fr_extension.d.ts.map +1 -0
  43. package/dest/msgpackr_fr_extension.js +21 -0
  44. package/dest/oracle/interfaces.d.ts +33 -8
  45. package/dest/oracle/interfaces.d.ts.map +1 -1
  46. package/dest/oracle/test-resolver/fixtures.d.ts +43 -0
  47. package/dest/oracle/test-resolver/fixtures.d.ts.map +1 -0
  48. package/dest/oracle/test-resolver/fixtures.js +39 -0
  49. package/dest/oracle/test-resolver/index.d.ts +9 -0
  50. package/dest/oracle/test-resolver/index.d.ts.map +1 -0
  51. package/dest/oracle/test-resolver/index.js +33 -0
  52. package/dest/oracle/test-resolver/resolver.d.ts +34 -0
  53. package/dest/oracle/test-resolver/resolver.d.ts.map +1 -0
  54. package/dest/oracle/test-resolver/resolver.js +114 -0
  55. package/dest/oracle/txe_oracle_public_context.d.ts +26 -2
  56. package/dest/oracle/txe_oracle_public_context.d.ts.map +1 -1
  57. package/dest/oracle/txe_oracle_public_context.js +43 -1
  58. package/dest/oracle/txe_oracle_registry.d.ts +14 -0
  59. package/dest/oracle/txe_oracle_registry.d.ts.map +1 -0
  60. package/dest/oracle/txe_oracle_registry.js +562 -0
  61. package/dest/oracle/txe_oracle_top_level_context.d.ts +32 -18
  62. package/dest/oracle/txe_oracle_top_level_context.d.ts.map +1 -1
  63. package/dest/oracle/txe_oracle_top_level_context.js +151 -55
  64. package/dest/oracle/txe_oracle_version.d.ts +17 -0
  65. package/dest/oracle/txe_oracle_version.d.ts.map +1 -0
  66. package/dest/oracle/txe_oracle_version.js +14 -0
  67. package/dest/oracle/txe_private_execution_oracle.d.ts +17 -0
  68. package/dest/oracle/txe_private_execution_oracle.d.ts.map +1 -0
  69. package/dest/oracle/txe_private_execution_oracle.js +15 -0
  70. package/dest/rpc_server.d.ts +14 -0
  71. package/dest/rpc_server.d.ts.map +1 -0
  72. package/dest/rpc_server.js +78 -0
  73. package/dest/rpc_translator.d.ts +103 -230
  74. package/dest/rpc_translator.d.ts.map +1 -1
  75. package/dest/rpc_translator.js +697 -616
  76. package/dest/server.bundle.js +3 -0
  77. package/dest/server.bundle.js.map +7 -0
  78. package/dest/state_machine/archiver.d.ts +4 -3
  79. package/dest/state_machine/archiver.d.ts.map +1 -1
  80. package/dest/state_machine/archiver.js +26 -15
  81. package/dest/state_machine/dummy_p2p_client.d.ts +14 -7
  82. package/dest/state_machine/dummy_p2p_client.d.ts.map +1 -1
  83. package/dest/state_machine/dummy_p2p_client.js +19 -4
  84. package/dest/state_machine/global_variable_builder.d.ts +9 -4
  85. package/dest/state_machine/global_variable_builder.d.ts.map +1 -1
  86. package/dest/state_machine/global_variable_builder.js +9 -3
  87. package/dest/state_machine/index.d.ts +4 -2
  88. package/dest/state_machine/index.d.ts.map +1 -1
  89. package/dest/state_machine/index.js +11 -3
  90. package/dest/state_machine/mock_epoch_cache.d.ts +16 -3
  91. package/dest/state_machine/mock_epoch_cache.d.ts.map +1 -1
  92. package/dest/state_machine/mock_epoch_cache.js +29 -2
  93. package/dest/state_machine/synchronizer.js +1 -1
  94. package/dest/txe_session.d.ts +85 -17
  95. package/dest/txe_session.d.ts.map +1 -1
  96. package/dest/txe_session.js +245 -40
  97. package/dest/utils/encoding.d.ts +191 -0
  98. package/dest/utils/encoding.d.ts.map +1 -0
  99. package/dest/{util → utils}/encoding.js +7 -2
  100. package/dest/{util → utils}/expected_failure_error.d.ts +1 -1
  101. package/dest/utils/expected_failure_error.d.ts.map +1 -0
  102. package/dest/{util → utils}/txe_account_store.d.ts +1 -1
  103. package/dest/utils/txe_account_store.d.ts.map +1 -0
  104. package/dest/utils/txe_artifact_resolver.d.ts +37 -0
  105. package/dest/utils/txe_artifact_resolver.d.ts.map +1 -0
  106. package/dest/utils/txe_artifact_resolver.js +161 -0
  107. package/dest/utils/txe_public_contract_data_source.d.ts +20 -0
  108. package/dest/utils/txe_public_contract_data_source.d.ts.map +1 -0
  109. package/dest/{util → utils}/txe_public_contract_data_source.js +1 -3
  110. package/dest/worker.bundle.js +3 -0
  111. package/dest/worker.bundle.js.map +7 -0
  112. package/dest/worker.d.ts +2 -0
  113. package/dest/worker.d.ts.map +1 -0
  114. package/dest/worker.js +92 -0
  115. package/package.json +38 -21
  116. package/src/bin/check_txe_oracle_version.ts +70 -0
  117. package/src/bin/index.ts +11 -2
  118. package/src/bin/oracle_test_server.ts +51 -0
  119. package/src/constants.ts +10 -0
  120. package/src/dispatcher_pool.ts +317 -0
  121. package/src/index.ts +97 -227
  122. package/src/msgpackr_fr_extension.ts +23 -0
  123. package/src/oracle/interfaces.ts +29 -7
  124. package/src/oracle/test-resolver/fixtures.ts +84 -0
  125. package/src/oracle/test-resolver/index.ts +45 -0
  126. package/src/oracle/test-resolver/resolver.ts +165 -0
  127. package/src/oracle/txe_oracle_public_context.ts +60 -0
  128. package/src/oracle/txe_oracle_registry.ts +401 -0
  129. package/src/oracle/txe_oracle_top_level_context.ts +185 -64
  130. package/src/oracle/txe_oracle_version.ts +17 -0
  131. package/src/oracle/txe_private_execution_oracle.ts +30 -0
  132. package/src/rpc_server.ts +87 -0
  133. package/src/rpc_translator.ts +767 -892
  134. package/src/state_machine/archiver.ts +38 -16
  135. package/src/state_machine/dummy_p2p_client.ts +35 -11
  136. package/src/state_machine/global_variable_builder.ts +18 -3
  137. package/src/state_machine/index.ts +17 -5
  138. package/src/state_machine/mock_epoch_cache.ts +38 -3
  139. package/src/state_machine/synchronizer.ts +1 -1
  140. package/src/txe_session.ts +437 -50
  141. package/src/{util → utils}/encoding.ts +8 -2
  142. package/src/utils/txe_artifact_resolver.ts +217 -0
  143. package/src/{util → utils}/txe_public_contract_data_source.ts +0 -2
  144. package/src/worker.ts +98 -0
  145. package/dest/util/encoding.d.ts +0 -720
  146. package/dest/util/encoding.d.ts.map +0 -1
  147. package/dest/util/expected_failure_error.d.ts.map +0 -1
  148. package/dest/util/txe_account_store.d.ts.map +0 -1
  149. package/dest/util/txe_public_contract_data_source.d.ts +0 -20
  150. package/dest/util/txe_public_contract_data_source.d.ts.map +0 -1
  151. /package/dest/{util → utils}/expected_failure_error.js +0 -0
  152. /package/dest/{util → utils}/txe_account_store.js +0 -0
  153. /package/src/{util → utils}/expected_failure_error.ts +0 -0
  154. /package/src/{util → utils}/txe_account_store.ts +0 -0
@@ -2,11 +2,12 @@ import { BlockNumber } from '@aztec/foundation/branded-types';
2
2
  import { Fr } from '@aztec/foundation/curves/bn254';
3
3
  import { type Logger, createLogger } from '@aztec/foundation/log';
4
4
  import { KeyStore } from '@aztec/key-store';
5
- import { openTmpStore } from '@aztec/kv-store/lmdb-v2';
6
- import type { AccessScopes } from '@aztec/pxe/client/lazy';
5
+ import type { AztecAsyncKVStore } from '@aztec/kv-store';
6
+ import { openEphemeralStore } from '@aztec/kv-store/lmdb-v2';
7
7
  import {
8
8
  AddressStore,
9
9
  AnchorBlockStore,
10
+ CapsuleService,
10
11
  CapsuleStore,
11
12
  ContractStore,
12
13
  JobCoordinator,
@@ -21,11 +22,13 @@ import {
21
22
  ExecutionNoteCache,
22
23
  ExecutionTaggingIndexCache,
23
24
  HashedValuesCache,
25
+ type IMiscOracle,
24
26
  type IPrivateExecutionOracle,
25
27
  type IUtilityExecutionOracle,
26
- Oracle,
27
- PrivateExecutionOracle,
28
+ Option,
29
+ TransientArrayService,
28
30
  UtilityExecutionOracle,
31
+ buildACIRCallback,
29
32
  } from '@aztec/pxe/simulator';
30
33
  import {
31
34
  ExecutionError,
@@ -35,28 +38,32 @@ import {
35
38
  resolveAssertionMessageFromError,
36
39
  toACVMWitness,
37
40
  } from '@aztec/simulator/client';
38
- import { FunctionCall, FunctionSelector, FunctionType } from '@aztec/stdlib/abi';
41
+ import { STANDARD_AUTH_REGISTRY_ADDRESS } from '@aztec/standard-contracts/auth-registry/constants';
42
+ import { EventSelector, FunctionCall, FunctionSelector, FunctionType } from '@aztec/stdlib/abi';
39
43
  import type { AuthWitness } from '@aztec/stdlib/auth-witness';
40
44
  import { AztecAddress } from '@aztec/stdlib/aztec-address';
41
- import { GasSettings } from '@aztec/stdlib/gas';
45
+ import type { GasSettings } from '@aztec/stdlib/gas';
42
46
  import { computeProtocolNullifier } from '@aztec/stdlib/hash';
43
47
  import { PrivateContextInputs } from '@aztec/stdlib/kernel';
44
48
  import { makeGlobalVariables } from '@aztec/stdlib/testing';
45
- import { CallContext, GlobalVariables, TxContext } from '@aztec/stdlib/tx';
49
+ import { CallContext, GlobalVariables, OFFCHAIN_MESSAGE_IDENTIFIER, TxContext } from '@aztec/stdlib/tx';
46
50
 
47
51
  import { z } from 'zod';
48
52
 
49
- import { DEFAULT_ADDRESS } from './constants.js';
53
+ import { DEFAULT_ADDRESS, MAX_OFFCHAIN_EFFECTS_PER_TXE_QUERY, MAX_OFFCHAIN_EFFECT_LEN } from './constants.js';
50
54
  import type { IAvmExecutionOracle, ITxeExecutionOracle } from './oracle/interfaces.js';
51
55
  import { TXEOraclePublicContext } from './oracle/txe_oracle_public_context.js';
52
56
  import { TXEOracleTopLevelContext } from './oracle/txe_oracle_top_level_context.js';
53
- import { RPCTranslator } from './rpc_translator.js';
57
+ import { TXE_ORACLE_VERSION_MAJOR, TXE_ORACLE_VERSION_MINOR } from './oracle/txe_oracle_version.js';
58
+ import { TXEPrivateExecutionOracle } from './oracle/txe_private_execution_oracle.js';
59
+ import { RPCTranslator, UnavailableOracleError } from './rpc_translator.js';
54
60
  import { TXEArchiver } from './state_machine/archiver.js';
55
61
  import { TXEStateMachine } from './state_machine/index.js';
56
- import type { ForeignCallArgs, ForeignCallResult } from './util/encoding.js';
57
- import { TXEAccountStore } from './util/txe_account_store.js';
58
62
  import { getSingleTxBlockRequestHash, insertTxEffectIntoWorldTrees, makeTXEBlock } from './utils/block_creation.js';
63
+ import type { ForeignCallArgs, ForeignCallResult } from './utils/encoding.js';
59
64
  import { makeTxEffect } from './utils/tx_effect_creation.js';
65
+ import { TXEAccountStore } from './utils/txe_account_store.js';
66
+ import type { TXEArtifactResolver } from './utils/txe_artifact_resolver.js';
60
67
 
61
68
  /**
62
69
  * A TXE Session can be in one of four states, which change as the test progresses and different oracles are called.
@@ -109,14 +116,110 @@ export type TXEOracleFunctionName = Exclude<
109
116
  >;
110
117
 
111
118
  export interface TXESessionStateHandler {
119
+ /** Records the TXE oracle version reported by the Noir test code for diagnostics. */
120
+ setTxeOracleVersion(major: number, minor: number): void;
121
+
112
122
  enterTopLevelState(): Promise<void>;
113
- enterPublicState(contractAddress?: AztecAddress): Promise<void>;
114
- enterPrivateState(contractAddress?: AztecAddress, anchorBlockNumber?: BlockNumber): Promise<PrivateContextInputs>;
115
- enterUtilityState(contractAddress?: AztecAddress): Promise<void>;
123
+ enterPublicState(contractAddress: Option<AztecAddress>): Promise<void>;
124
+ enterPrivateState(
125
+ contractAddress: Option<AztecAddress>,
126
+ anchorBlockNumber: Option<BlockNumber>,
127
+ gasSettings: GasSettings,
128
+ ): Promise<PrivateContextInputs>;
129
+ enterUtilityState(contractAddress: Option<AztecAddress>): Promise<void>;
130
+
131
+ /**
132
+ * Executes a top-level private call: runs the private function, drains its offchain effects into the session buffer,
133
+ * commits the job, and (for non-static calls) tags the result with the mined tx hash.
134
+ */
135
+ executePrivateCall(
136
+ from: Option<AztecAddress>,
137
+ targetContractAddress: AztecAddress,
138
+ functionSelector: FunctionSelector,
139
+ args: Fr[],
140
+ argsHash: Fr,
141
+ isStaticCall: boolean,
142
+ additionalScopes: AztecAddress[],
143
+ authorizedUtilityCallTargets: AztecAddress[],
144
+ gasSettings: GasSettings,
145
+ ): Promise<Fr[]>;
146
+
147
+ /** Executes a top-level utility function and commits the job. */
148
+ executeUtilityFunction(
149
+ targetContractAddress: AztecAddress,
150
+ functionSelector: FunctionSelector,
151
+ args: Fr[],
152
+ authorizedUtilityCallTargets: AztecAddress[],
153
+ ): Promise<Fr[]>;
154
+
155
+ /**
156
+ * Executes a top-level public call, commits the job, and (for non-static calls) tags the result with the mined tx
157
+ * hash.
158
+ */
159
+ executePublicCall(
160
+ from: Option<AztecAddress>,
161
+ targetContractAddress: AztecAddress,
162
+ calldata: Fr[],
163
+ isStaticCall: boolean,
164
+ gasSettings: GasSettings,
165
+ ): Promise<Fr[]>;
166
+
167
+ /** Syncs the target contract and returns the private events it emitted matching the given selector and scope. */
168
+ getPrivateEvents(selector: EventSelector, contractAddress: AztecAddress, scope: AztecAddress): Promise<Fr[][]>;
169
+
170
+ /**
171
+ * Captures a raw offchain effect payload for consumption from test environment. Called by the `emit_offchain_effect`
172
+ * oracle handler whenever a contract function emits an offchain message, at any call depth.
173
+ */
174
+ recordOffchainEffect(data: Fr[]): void;
175
+
176
+ /**
177
+ * Returns the raw offchain effect payloads emitted by the last top-level call. Each payload follows the protocol
178
+ * convention documented on `OFFCHAIN_MESSAGE_IDENTIFIER`, i.e. `[identifier, recipient, ...ciphertext]`. Decoding into
179
+ * `OffchainMessage` structs happens on the Noir side of the test helper. Marks the buffer as queried so the
180
+ * unqueried-messages warning doesn't fire on the next reset.
181
+ */
182
+ getLastCallOffchainEffects(): { effects: Fr[][] };
183
+
184
+ /**
185
+ * Returns the context of the last top-level call: its tx hash (`Fr.ZERO` if the call was tx-less) and the anchor
186
+ * block timestamp captured at the start of the call. Does *not* mark the buffer as queried — context reads are
187
+ * metadata, not effect consumption.
188
+ */
189
+ getLastCallContext(): { txHash: Fr; anchorBlockTimestamp: bigint };
190
+ }
191
+
192
+ /**
193
+ * Session state tracking the most recently completed top-level call: the offchain effect buffer it produced, and the
194
+ * call's context (tx hash + anchor block timestamp). The context is refreshed on every top-level call, independently
195
+ * of whether the call produced offchain effects.
196
+ */
197
+ interface LastCallState {
198
+ /**
199
+ * Raw offchain effect payloads emitted by the currently-executing (or most recently completed) top-level call. Wiped
200
+ * at the start of every top-level entry point, appended to on every `emit_offchain_effect` oracle invocation.
201
+ */
202
+ offchainEffects: Fr[][];
203
+ /**
204
+ * Tracks whether the test has queried `effects` since the last reset. If a new top-level call clobbers the buffer
205
+ * without it being queried first, any accumulated messages are lost and we emit a warning so tests don't silently
206
+ * drop delivery.
207
+ */
208
+ queried: boolean;
209
+ /**
210
+ * Tx hash of the most recently completed top-level call, or `Fr.ZERO` if the call was tx-less (context setters,
211
+ * utility execution). Populated by call executor handlers after execution completes.
212
+ */
213
+ txHash: Fr;
214
+ /**
215
+ * Anchor block timestamp of the most recently completed top-level call, captured from the anchor block header that
216
+ * was active when the call started. Populated by call executor handlers after execution completes.
217
+ */
218
+ anchorBlockTimestamp: bigint;
219
+ }
116
220
 
117
- // TODO(F-335): Exposing the job info is abstraction breakage - drop the following 2 functions.
118
- cycleJob(): Promise<string>;
119
- getCurrentJob(): string;
221
+ function emptyLastCallState(): LastCallState {
222
+ return { offchainEffects: [], queried: false, txHash: Fr.ZERO, anchorBlockTimestamp: 0n };
120
223
  }
121
224
 
122
225
  /**
@@ -126,11 +229,17 @@ export interface TXESessionStateHandler {
126
229
  export class TXESession implements TXESessionStateHandler {
127
230
  private state: SessionState = { name: 'TOP_LEVEL' };
128
231
  private authwits: Map<string, AuthWitness> = new Map();
232
+ private lastCallInfo: LastCallState = emptyLastCallState();
233
+ private txeOracleVersion: { major: number; minor: number } | undefined;
234
+
235
+ private disposed = false;
129
236
 
130
237
  constructor(
131
238
  private logger: Logger,
239
+ private sessionStore: AztecAsyncKVStore,
132
240
  private stateMachine: TXEStateMachine,
133
241
  private oracleHandler:
242
+ | IMiscOracle
134
243
  | IUtilityExecutionOracle
135
244
  | IPrivateExecutionOracle
136
245
  | IAvmExecutionOracle
@@ -150,10 +259,43 @@ export class TXESession implements TXESessionStateHandler {
150
259
  private chainId: Fr,
151
260
  private version: Fr,
152
261
  private nextBlockTimestamp: bigint,
262
+ private readonly artifactResolver: TXEArtifactResolver,
263
+ private readonly rootPath: string,
264
+ private readonly packageName: string,
153
265
  ) {}
154
266
 
155
- static async init(contractStore: ContractStore) {
156
- const store = await openTmpStore('txe-session');
267
+ /**
268
+ * Closes the per-session `txe-session` LMDB and the `NativeWorldStateService` .
269
+ * Called via IPC when the dispatcher detects the end of a test. Idempotent.
270
+ */
271
+ async dispose(): Promise<void> {
272
+ if (this.disposed) {
273
+ return;
274
+ }
275
+ this.disposed = true;
276
+ try {
277
+ await this.stateMachine.synchronizer.nativeWorldStateService.close();
278
+ } catch (err) {
279
+ this.logger.warn(`Error closing native world state during session dispose`, err);
280
+ }
281
+ try {
282
+ await this.sessionStore.close();
283
+ } catch (err) {
284
+ this.logger.warn(`Error closing session LMDB during dispose`, err);
285
+ }
286
+ }
287
+
288
+ static async init(
289
+ contractStore: ContractStore,
290
+ artifactResolver: TXEArtifactResolver,
291
+ rootPath: string,
292
+ packageName: string,
293
+ ) {
294
+ // Size LMDB's reader slots to the libuv pool (capped to 2 in bin/index.ts via
295
+ // HARDWARE_CONCURRENCY): each native LMDB read needs a libuv worker thread to run, so any
296
+ // slot beyond the pool size would sit idle while still consuming a semaphore + reader-table
297
+ // entry per session.
298
+ const store = await openEphemeralStore('txe-session', undefined, 2);
157
299
 
158
300
  const addressStore = new AddressStore(store);
159
301
  const privateEventStore = new PrivateEventStore(store);
@@ -165,7 +307,6 @@ export class TXESession implements TXESessionStateHandler {
165
307
  const keyStore = new KeyStore(store);
166
308
  const accountStore = new TXEAccountStore(store);
167
309
 
168
- // Create job coordinator and register staged stores
169
310
  const jobCoordinator = new JobCoordinator(store);
170
311
  jobCoordinator.registerStores([
171
312
  capsuleStore,
@@ -185,6 +326,8 @@ export class TXESession implements TXESessionStateHandler {
185
326
 
186
327
  const initialJobId = jobCoordinator.beginJob();
187
328
 
329
+ const logger = createLogger('txe:session');
330
+
188
331
  const topLevelOracleHandler = new TXEOracleTopLevelContext(
189
332
  stateMachine,
190
333
  contractStore,
@@ -201,11 +344,16 @@ export class TXESession implements TXESessionStateHandler {
201
344
  version,
202
345
  chainId,
203
346
  new Map(),
347
+ artifactResolver,
348
+ rootPath,
349
+ packageName,
204
350
  );
205
- await topLevelOracleHandler.advanceBlocksBy(1);
351
+
352
+ await topLevelOracleHandler.mineDeploymentNullifiers([STANDARD_AUTH_REGISTRY_ADDRESS]);
206
353
 
207
354
  return new TXESession(
208
- createLogger('txe:session'),
355
+ logger,
356
+ store,
209
357
  stateMachine,
210
358
  topLevelOracleHandler,
211
359
  contractStore,
@@ -223,6 +371,9 @@ export class TXESession implements TXESessionStateHandler {
223
371
  version,
224
372
  chainId,
225
373
  nextBlockTimestamp,
374
+ artifactResolver,
375
+ rootPath,
376
+ packageName,
226
377
  );
227
378
  }
228
379
 
@@ -244,7 +395,28 @@ export class TXESession implements TXESessionStateHandler {
244
395
  return translator[validatedFunctionName](...inputs);
245
396
  } catch (error) {
246
397
  if (error instanceof z.ZodError) {
247
- throw new Error(`${functionName} does not correspond to any oracle handler available on RPCTranslator`);
398
+ let versionHint: string;
399
+ if (!this.txeOracleVersion) {
400
+ versionHint =
401
+ ' The test appears to use an older version of Aztec.nr that does not' +
402
+ ' support test environment oracle versioning. Update Aztec.nr to a compatible version.' +
403
+ ' See https://docs.aztec.network/errors/12';
404
+ } else if (this.txeOracleVersion.minor > TXE_ORACLE_VERSION_MINOR) {
405
+ versionHint =
406
+ ` The test uses Aztec.nr test oracle version` +
407
+ ` ${this.txeOracleVersion.major}.${this.txeOracleVersion.minor}, but this test environment` +
408
+ ` only supports up to ${TXE_ORACLE_VERSION_MAJOR}.${TXE_ORACLE_VERSION_MINOR}.` +
409
+ ` Upgrade the Aztec CLI to a compatible version.` +
410
+ ` See https://docs.aztec.network/errors/12`;
411
+ } else {
412
+ versionHint =
413
+ ` The test's oracle version (${this.txeOracleVersion.major}.${this.txeOracleVersion.minor})` +
414
+ ` is compatible with this test environment` +
415
+ ` (${TXE_ORACLE_VERSION_MAJOR}.${TXE_ORACLE_VERSION_MINOR}), so this oracle should be` +
416
+ ` available. This is an unexpected error, please report it.` +
417
+ ` See https://docs.aztec.network/errors/13`;
418
+ }
419
+ throw new Error(`Unknown oracle '${functionName}'.${versionHint}`);
248
420
  } else if (error instanceof Error) {
249
421
  throw new Error(
250
422
  `Execution error while processing function ${functionName} in state ${this.state.name}: ${error.message}`,
@@ -257,17 +429,193 @@ export class TXESession implements TXESessionStateHandler {
257
429
  }
258
430
  }
259
431
 
260
- getCurrentJob(): string {
261
- return this.currentJobId;
262
- }
263
-
264
432
  /** Commits the current job and begins a new one. Returns the new job ID. */
265
- async cycleJob(): Promise<string> {
433
+ private async cycleJob(): Promise<string> {
266
434
  await this.jobCoordinator.commitJob(this.currentJobId);
267
435
  this.currentJobId = this.jobCoordinator.beginJob();
268
436
  return this.currentJobId;
269
437
  }
270
438
 
439
+ private resetLastCall(): void {
440
+ const notQueriedMessageCount = this.lastCallInfo.queried
441
+ ? 0
442
+ : this.lastCallInfo.offchainEffects.filter(payload => payload[0]?.equals(OFFCHAIN_MESSAGE_IDENTIFIER)).length;
443
+ if (notQueriedMessageCount > 0) {
444
+ this.logger.warn(
445
+ `Dropping ${notQueriedMessageCount} unqueried offchain message(s) from the previous top-level call. ` +
446
+ `To deliver them, call \`env.offchain_messages()\` and forward the result to the recipient contract's ` +
447
+ `\`offchain_receive\` utility before issuing another top-level call. To intentionally discard, assign ` +
448
+ `to \`let _ = env.offchain_messages()\` to silence this warning.`,
449
+ );
450
+ }
451
+ this.lastCallInfo = emptyLastCallState();
452
+ }
453
+
454
+ recordOffchainEffect(data: Fr[]): void {
455
+ this.lastCallInfo.offchainEffects.push(data);
456
+ }
457
+
458
+ private setLastCallContext(txHash: Fr, anchorBlockTimestamp: bigint): void {
459
+ this.lastCallInfo.txHash = txHash;
460
+ this.lastCallInfo.anchorBlockTimestamp = anchorBlockTimestamp;
461
+ }
462
+
463
+ private async withTopLevelCallTracking<T>(work: () => Promise<{ result: T; txHash?: Fr }>): Promise<T> {
464
+ this.resetLastCall();
465
+ // Capture the anchor *before* `work` runs: private/public executor calls mine a new block as a
466
+ // side effect, and that block's timestamp should not be attributed to this call's anchor.
467
+ const anchorBlockTimestamp = (await this.stateMachine.node.getBlockData('latest'))!.header.globalVariables
468
+ .timestamp;
469
+ const { result, txHash } = await work();
470
+ this.setLastCallContext(txHash ?? Fr.ZERO, anchorBlockTimestamp);
471
+ return result;
472
+ }
473
+
474
+ getLastCallOffchainEffects(): { effects: Fr[][] } {
475
+ this.lastCallInfo.queried = true;
476
+ const effects = this.lastCallInfo.offchainEffects;
477
+
478
+ if (effects.length > MAX_OFFCHAIN_EFFECTS_PER_TXE_QUERY) {
479
+ throw new Error(`${effects.length} offchain effects exceed max ${MAX_OFFCHAIN_EFFECTS_PER_TXE_QUERY}`);
480
+ }
481
+ if (effects.some(e => e.length > MAX_OFFCHAIN_EFFECT_LEN)) {
482
+ throw new Error(`Some offchain effect has length larger than max ${MAX_OFFCHAIN_EFFECT_LEN}`);
483
+ }
484
+
485
+ return { effects };
486
+ }
487
+
488
+ getLastCallContext(): { txHash: Fr; anchorBlockTimestamp: bigint } {
489
+ const { txHash, anchorBlockTimestamp } = this.lastCallInfo;
490
+ return { txHash, anchorBlockTimestamp };
491
+ }
492
+
493
+ async executePrivateCall(
494
+ from: Option<AztecAddress>,
495
+ targetContractAddress: AztecAddress,
496
+ functionSelector: FunctionSelector,
497
+ args: Fr[],
498
+ argsHash: Fr,
499
+ isStaticCall: boolean,
500
+ additionalScopes: AztecAddress[],
501
+ authorizedUtilityCallTargets: AztecAddress[],
502
+ gasSettings: GasSettings,
503
+ ): Promise<Fr[]> {
504
+ const handler = this.handlerAsTxe();
505
+ return await this.withTopLevelCallTracking(async () => {
506
+ const { returnValues, offchainEffects } = await handler.privateCallNewFlow(
507
+ from?.value,
508
+ targetContractAddress,
509
+ functionSelector,
510
+ args,
511
+ argsHash,
512
+ isStaticCall,
513
+ additionalScopes,
514
+ this.currentJobId,
515
+ authorizedUtilityCallTargets,
516
+ gasSettings,
517
+ );
518
+
519
+ // Private execution collects offchain effects inside PXE's PrivateExecutionOracle rather than round-tripping
520
+ // them through `aztec_utl_emitOffchainEffect`, so the session buffer is empty at this point. Drain the effects
521
+ // from the execution tree into the session buffer so the next `env.offchain_messages()` call in the test sees
522
+ // them.
523
+ for (const data of offchainEffects) {
524
+ this.recordOffchainEffect(data);
525
+ }
526
+
527
+ await this.cycleJob();
528
+
529
+ if (isStaticCall) {
530
+ // Static calls revert their checkpoint and mine no block, so there is no tx hash to tag offchain effects
531
+ // with. Querying `getLastTxEffects()` here would return an unrelated predecessor tx.
532
+ return { result: returnValues };
533
+ }
534
+ const { txHash } = await handler.getLastTxEffects();
535
+ return { result: returnValues, txHash: txHash.hash };
536
+ });
537
+ }
538
+
539
+ async executeUtilityFunction(
540
+ targetContractAddress: AztecAddress,
541
+ functionSelector: FunctionSelector,
542
+ args: Fr[],
543
+ authorizedUtilityCallTargets: AztecAddress[],
544
+ ): Promise<Fr[]> {
545
+ const handler = this.handlerAsTxe();
546
+ return await this.withTopLevelCallTracking(async () => {
547
+ const returnValues = await handler.executeUtilityFunction(
548
+ targetContractAddress,
549
+ functionSelector,
550
+ args,
551
+ this.currentJobId,
552
+ authorizedUtilityCallTargets,
553
+ );
554
+
555
+ await this.cycleJob();
556
+
557
+ return { result: returnValues };
558
+ });
559
+ }
560
+
561
+ async executePublicCall(
562
+ from: Option<AztecAddress>,
563
+ targetContractAddress: AztecAddress,
564
+ calldata: Fr[],
565
+ isStaticCall: boolean,
566
+ gasSettings: GasSettings,
567
+ ): Promise<Fr[]> {
568
+ const handler = this.handlerAsTxe();
569
+ return await this.withTopLevelCallTracking(async () => {
570
+ const returnValues = await handler.publicCallNewFlow(
571
+ from?.value,
572
+ targetContractAddress,
573
+ calldata,
574
+ isStaticCall,
575
+ gasSettings,
576
+ );
577
+
578
+ await this.cycleJob();
579
+
580
+ if (isStaticCall) {
581
+ // See the equivalent branch in `executePrivateCall`.
582
+ return { result: returnValues };
583
+ }
584
+ const { txHash } = await handler.getLastTxEffects();
585
+ return { result: returnValues, txHash: txHash.hash };
586
+ });
587
+ }
588
+
589
+ async getPrivateEvents(selector: EventSelector, contractAddress: AztecAddress, scope: AztecAddress): Promise<Fr[][]> {
590
+ const handler = this.handlerAsTxe();
591
+ await handler.syncContractNonOracleMethod(contractAddress, scope, this.currentJobId);
592
+ // Cycle the job to commit the stores after the contract sync.
593
+ await this.cycleJob();
594
+ return handler.getPrivateEvents(selector, contractAddress, scope);
595
+ }
596
+
597
+ private handlerAsTxe(): ITxeExecutionOracle {
598
+ if (!('isTxe' in this.oracleHandler)) {
599
+ throw new UnavailableOracleError('Txe');
600
+ }
601
+ return this.oracleHandler;
602
+ }
603
+
604
+ setTxeOracleVersion(major: number, minor: number): void {
605
+ if (major !== TXE_ORACLE_VERSION_MAJOR) {
606
+ const hint =
607
+ major > TXE_ORACLE_VERSION_MAJOR
608
+ ? 'The test was compiled with a newer version of Aztec.nr than your test environment supports. Upgrade your test environment to a compatible version.'
609
+ : 'The test was compiled with an older version of Aztec.nr than your test environment supports. Recompile the test with a compatible version of Aztec.nr.';
610
+ throw new Error(
611
+ `Incompatible test environment version: ${hint} See https://docs.aztec.network/errors/12 (expected test oracle major version ${TXE_ORACLE_VERSION_MAJOR}, got ${major})`,
612
+ );
613
+ }
614
+
615
+ this.txeOracleVersion = { major, minor };
616
+ this.logger.debug(`Test compiled with test oracle version ${major}.${minor}`);
617
+ }
618
+
271
619
  async enterTopLevelState() {
272
620
  switch (this.state.name) {
273
621
  case 'PRIVATE': {
@@ -309,6 +657,9 @@ export class TXESession implements TXESessionStateHandler {
309
657
  this.version,
310
658
  this.chainId,
311
659
  this.authwits,
660
+ this.artifactResolver,
661
+ this.rootPath,
662
+ this.packageName,
312
663
  );
313
664
 
314
665
  this.state = { name: 'TOP_LEVEL' };
@@ -316,21 +667,25 @@ export class TXESession implements TXESessionStateHandler {
316
667
  }
317
668
 
318
669
  async enterPrivateState(
319
- contractAddress: AztecAddress = DEFAULT_ADDRESS,
320
- anchorBlockNumber?: BlockNumber,
670
+ contractAddressOpt: Option<AztecAddress>,
671
+ anchorBlockNumberOpt: Option<BlockNumber>,
672
+ gasSettings: GasSettings,
321
673
  ): Promise<PrivateContextInputs> {
674
+ const contractAddress = contractAddressOpt?.value ?? DEFAULT_ADDRESS;
675
+ const anchorBlockNumber = anchorBlockNumberOpt?.value;
322
676
  this.exitTopLevelState();
677
+ this.resetLastCall();
323
678
 
324
679
  // Private execution has two associated block numbers: the anchor block (i.e. the historical block that is used to
325
680
  // build the proof), and the *next* block, i.e. the one we'll create once the execution ends, and which will contain
326
681
  // a single transaction with the effects of what was done in the test.
327
- const anchorBlock = await this.stateMachine.node.getBlockHeader(anchorBlockNumber ?? 'latest');
682
+ const anchorBlock = await this.stateMachine.node.getBlock(anchorBlockNumber ?? 'latest').then(b => b?.header);
328
683
 
329
684
  await new NoteService(this.noteStore, this.stateMachine.node, anchorBlock!, this.currentJobId).syncNoteNullifiers(
330
685
  contractAddress,
331
- 'ALL_SCOPES',
686
+ await this.keyStore.getAccounts(),
332
687
  );
333
- const latestBlock = await this.stateMachine.node.getBlockHeader('latest');
688
+ const latestBlock = await this.stateMachine.node.getBlock('latest').then(b => b?.header);
334
689
 
335
690
  const nextBlockGlobalVariables = makeGlobalVariables(undefined, {
336
691
  blockNumber: BlockNumber(latestBlock!.globalVariables.blockNumber + 1),
@@ -345,9 +700,10 @@ export class TXESession implements TXESessionStateHandler {
345
700
  const taggingIndexCache = new ExecutionTaggingIndexCache();
346
701
 
347
702
  const utilityExecutor = this.utilityExecutorForContractSync(anchorBlock);
348
- this.oracleHandler = new PrivateExecutionOracle({
703
+ const transientArrayService = new TransientArrayService();
704
+ this.oracleHandler = new TXEPrivateExecutionOracle({
349
705
  argsHash: Fr.ZERO,
350
- txContext: new TxContext(this.chainId, this.version, GasSettings.empty()),
706
+ txContext: new TxContext(this.chainId, this.version, gasSettings),
351
707
  callContext: new CallContext(AztecAddress.ZERO, contractAddress, FunctionSelector.empty(), false),
352
708
  anchorBlockHeader: anchorBlock!,
353
709
  utilityExecutor,
@@ -364,12 +720,15 @@ export class TXESession implements TXESessionStateHandler {
364
720
  senderTaggingStore: this.senderTaggingStore,
365
721
  recipientTaggingStore: this.recipientTaggingStore,
366
722
  senderAddressBookStore: this.senderAddressBookStore,
367
- capsuleStore: this.capsuleStore,
723
+ capsuleService: new CapsuleService(this.capsuleStore, await this.keyStore.getAccounts()),
368
724
  privateEventStore: this.privateEventStore,
369
725
  contractSyncService: this.stateMachine.contractSyncService,
726
+ l2TipsStore: this.stateMachine.l2TipsProvider,
370
727
  jobId: this.currentJobId,
371
- scopes: 'ALL_SCOPES',
728
+ scopes: await this.keyStore.getAccounts(),
372
729
  messageContextService: this.stateMachine.messageContextService,
730
+ simulator: new WASMSimulator(),
731
+ transientArrayService,
373
732
  });
374
733
 
375
734
  // We store the note and tagging index caches fed into the PrivateExecutionOracle (along with some other auxiliary
@@ -380,35 +739,47 @@ export class TXESession implements TXESessionStateHandler {
380
739
  this.state = { name: 'PRIVATE', nextBlockGlobalVariables, noteCache, taggingIndexCache };
381
740
  this.logger.debug(`Entered state ${this.state.name}`);
382
741
 
383
- return (this.oracleHandler as PrivateExecutionOracle).getPrivateContextInputs();
742
+ // Record the *resolved* anchor's timestamp — if the caller pinned the anchor to a past block
743
+ // via `anchorBlockNumber`, "latest" would be the wrong anchor for offchain-message semantics.
744
+ this.setLastCallContext(Fr.ZERO, anchorBlock!.globalVariables.timestamp);
745
+
746
+ return (this.oracleHandler as TXEPrivateExecutionOracle).getPrivateContextInputs();
384
747
  }
385
748
 
386
- async enterPublicState(contractAddress?: AztecAddress) {
749
+ async enterPublicState(contractAddressOpt: Option<AztecAddress>) {
750
+ const contractAddress = contractAddressOpt?.value ?? DEFAULT_ADDRESS;
387
751
  this.exitTopLevelState();
752
+ this.resetLastCall();
388
753
 
389
754
  // The PublicContext will create a block with a single transaction in it, containing the effects of what was done in
390
755
  // the test. The block therefore gets the *next* block number and timestamp.
391
- const latestBlockNumber = (await this.stateMachine.node.getBlockHeader('latest'))!.globalVariables.blockNumber;
756
+ const latestHeader = (await this.stateMachine.node.getBlockData('latest'))!.header;
392
757
  const globalVariables = makeGlobalVariables(undefined, {
393
- blockNumber: BlockNumber(latestBlockNumber + 1),
758
+ blockNumber: BlockNumber(latestHeader.globalVariables.blockNumber + 1),
394
759
  timestamp: this.nextBlockTimestamp,
395
760
  version: this.version,
396
761
  chainId: this.chainId,
397
762
  });
398
763
 
399
764
  this.oracleHandler = new TXEOraclePublicContext(
400
- contractAddress ?? DEFAULT_ADDRESS,
765
+ contractAddress,
401
766
  await this.stateMachine.synchronizer.nativeWorldStateService.fork(),
402
767
  getSingleTxBlockRequestHash(globalVariables.blockNumber),
403
768
  globalVariables,
769
+ this.contractStore,
404
770
  );
405
771
 
406
772
  this.state = { name: 'PUBLIC' };
407
773
  this.logger.debug(`Entered state ${this.state.name}`);
774
+
775
+ // Public state is anchored at the latest block.
776
+ this.setLastCallContext(Fr.ZERO, latestHeader.globalVariables.timestamp);
408
777
  }
409
778
 
410
- async enterUtilityState(contractAddress: AztecAddress = DEFAULT_ADDRESS) {
779
+ async enterUtilityState(contractAddressOpt: Option<AztecAddress>) {
780
+ const contractAddress = contractAddressOpt?.value ?? DEFAULT_ADDRESS;
411
781
  this.exitTopLevelState();
782
+ this.resetLastCall();
412
783
 
413
784
  const anchorBlockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
414
785
 
@@ -422,7 +793,7 @@ export class TXESession implements TXESessionStateHandler {
422
793
  this.stateMachine.node,
423
794
  anchorBlockHeader,
424
795
  this.currentJobId,
425
- ).syncNoteNullifiers(contractAddress, 'ALL_SCOPES');
796
+ ).syncNoteNullifiers(contractAddress, await this.keyStore.getAccounts());
426
797
 
427
798
  this.oracleHandler = new UtilityExecutionOracle({
428
799
  contractAddress,
@@ -436,15 +807,24 @@ export class TXESession implements TXESessionStateHandler {
436
807
  aztecNode: this.stateMachine.node,
437
808
  recipientTaggingStore: this.recipientTaggingStore,
438
809
  senderAddressBookStore: this.senderAddressBookStore,
439
- capsuleStore: this.capsuleStore,
810
+ capsuleService: new CapsuleService(this.capsuleStore, await this.keyStore.getAccounts()),
440
811
  privateEventStore: this.privateEventStore,
441
812
  messageContextService: this.stateMachine.messageContextService,
813
+ contractSyncService: this.stateMachine.contractSyncService,
814
+ l2TipsStore: this.stateMachine.l2TipsProvider,
442
815
  jobId: this.currentJobId,
443
- scopes: 'ALL_SCOPES',
816
+ scopes: await this.keyStore.getAccounts(),
817
+ simulator: new WASMSimulator(),
818
+ utilityExecutor: this.utilityExecutorForContractSync(anchorBlockHeader),
819
+ // Execution-tree root (top-level utility run): own store; nested frames inherit it.
820
+ transientArrayService: new TransientArrayService(),
444
821
  });
445
822
 
446
823
  this.state = { name: 'UTILITY' };
447
824
  this.logger.debug(`Entered state ${this.state.name}`);
825
+
826
+ // Utility state anchors at whatever the anchor block store is pointing to (tracked as latest).
827
+ this.setLastCallContext(Fr.ZERO, anchorBlockHeader.globalVariables.timestamp);
448
828
  }
449
829
 
450
830
  private exitTopLevelState() {
@@ -509,13 +889,14 @@ export class TXESession implements TXESessionStateHandler {
509
889
  }
510
890
 
511
891
  private utilityExecutorForContractSync(anchorBlock: any) {
512
- return async (call: FunctionCall, scopes: AccessScopes) => {
892
+ return async (call: FunctionCall, scopes: AztecAddress[]) => {
513
893
  const entryPointArtifact = await this.contractStore.getFunctionArtifactWithDebugMetadata(call.to, call.selector);
514
894
  if (entryPointArtifact.functionType !== FunctionType.UTILITY) {
515
895
  throw new Error(`Cannot run ${entryPointArtifact.functionType} function as utility`);
516
896
  }
517
897
 
518
898
  try {
899
+ const simulator = new WASMSimulator();
519
900
  const oracle = new UtilityExecutionOracle({
520
901
  contractAddress: call.to,
521
902
  authWitnesses: [],
@@ -528,14 +909,20 @@ export class TXESession implements TXESessionStateHandler {
528
909
  aztecNode: this.stateMachine.node,
529
910
  recipientTaggingStore: this.recipientTaggingStore,
530
911
  senderAddressBookStore: this.senderAddressBookStore,
531
- capsuleStore: this.capsuleStore,
912
+ capsuleService: new CapsuleService(this.capsuleStore, scopes),
532
913
  privateEventStore: this.privateEventStore,
533
914
  messageContextService: this.stateMachine.messageContextService,
915
+ contractSyncService: this.stateMachine.contractSyncService,
916
+ l2TipsStore: this.stateMachine.l2TipsProvider,
534
917
  jobId: this.currentJobId,
535
918
  scopes,
919
+ simulator,
920
+ utilityExecutor: this.utilityExecutorForContractSync(anchorBlock),
921
+ // Top-level utility entrypoint: gets a fresh store. Nested frames inherit it via UtilityExecutionOracle.
922
+ transientArrayService: new TransientArrayService(),
536
923
  });
537
- await new WASMSimulator()
538
- .executeUserCircuit(toACVMWitness(0, call.args), entryPointArtifact, new Oracle(oracle).toACIRCallback())
924
+ await simulator
925
+ .executeUserCircuit(toACVMWitness(0, call.args), entryPointArtifact, buildACIRCallback(oracle))
539
926
  .catch((err: Error) => {
540
927
  err.message = resolveAssertionMessageFromError(err, entryPointArtifact);
541
928
  throw new ExecutionError(