@aztec/txe 5.0.0-private.20260319 → 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 +33 -20
  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 -58
  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 -233
  74. package/dest/rpc_translator.d.ts.map +1 -1
  75. package/dest/rpc_translator.js +695 -636
  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 +1 -2
  91. package/dest/state_machine/mock_epoch_cache.d.ts.map +1 -1
  92. package/dest/state_machine/mock_epoch_cache.js +0 -3
  93. package/dest/state_machine/synchronizer.js +1 -1
  94. package/dest/txe_session.d.ts +86 -19
  95. package/dest/txe_session.d.ts.map +1 -1
  96. package/dest/txe_session.js +244 -45
  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 -67
  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 +765 -913
  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 +0 -4
  139. package/src/state_machine/synchronizer.ts +1 -1
  140. package/src/txe_session.ts +434 -57
  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,14 +2,14 @@ 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
- ContractSyncService,
13
13
  JobCoordinator,
14
14
  NoteService,
15
15
  NoteStore,
@@ -22,11 +22,13 @@ import {
22
22
  ExecutionNoteCache,
23
23
  ExecutionTaggingIndexCache,
24
24
  HashedValuesCache,
25
+ type IMiscOracle,
25
26
  type IPrivateExecutionOracle,
26
27
  type IUtilityExecutionOracle,
27
- Oracle,
28
- PrivateExecutionOracle,
28
+ Option,
29
+ TransientArrayService,
29
30
  UtilityExecutionOracle,
31
+ buildACIRCallback,
30
32
  } from '@aztec/pxe/simulator';
31
33
  import {
32
34
  ExecutionError,
@@ -36,28 +38,32 @@ import {
36
38
  resolveAssertionMessageFromError,
37
39
  toACVMWitness,
38
40
  } from '@aztec/simulator/client';
39
- 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';
40
43
  import type { AuthWitness } from '@aztec/stdlib/auth-witness';
41
44
  import { AztecAddress } from '@aztec/stdlib/aztec-address';
42
- import { GasSettings } from '@aztec/stdlib/gas';
45
+ import type { GasSettings } from '@aztec/stdlib/gas';
43
46
  import { computeProtocolNullifier } from '@aztec/stdlib/hash';
44
47
  import { PrivateContextInputs } from '@aztec/stdlib/kernel';
45
48
  import { makeGlobalVariables } from '@aztec/stdlib/testing';
46
- import { CallContext, GlobalVariables, TxContext } from '@aztec/stdlib/tx';
49
+ import { CallContext, GlobalVariables, OFFCHAIN_MESSAGE_IDENTIFIER, TxContext } from '@aztec/stdlib/tx';
47
50
 
48
51
  import { z } from 'zod';
49
52
 
50
- import { DEFAULT_ADDRESS } from './constants.js';
53
+ import { DEFAULT_ADDRESS, MAX_OFFCHAIN_EFFECTS_PER_TXE_QUERY, MAX_OFFCHAIN_EFFECT_LEN } from './constants.js';
51
54
  import type { IAvmExecutionOracle, ITxeExecutionOracle } from './oracle/interfaces.js';
52
55
  import { TXEOraclePublicContext } from './oracle/txe_oracle_public_context.js';
53
56
  import { TXEOracleTopLevelContext } from './oracle/txe_oracle_top_level_context.js';
54
- 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';
55
60
  import { TXEArchiver } from './state_machine/archiver.js';
56
61
  import { TXEStateMachine } from './state_machine/index.js';
57
- import type { ForeignCallArgs, ForeignCallResult } from './util/encoding.js';
58
- import { TXEAccountStore } from './util/txe_account_store.js';
59
62
  import { getSingleTxBlockRequestHash, insertTxEffectIntoWorldTrees, makeTXEBlock } from './utils/block_creation.js';
63
+ import type { ForeignCallArgs, ForeignCallResult } from './utils/encoding.js';
60
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';
61
67
 
62
68
  /**
63
69
  * A TXE Session can be in one of four states, which change as the test progresses and different oracles are called.
@@ -110,14 +116,110 @@ export type TXEOracleFunctionName = Exclude<
110
116
  >;
111
117
 
112
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
+
113
122
  enterTopLevelState(): Promise<void>;
114
- enterPublicState(contractAddress?: AztecAddress): Promise<void>;
115
- enterPrivateState(contractAddress?: AztecAddress, anchorBlockNumber?: BlockNumber): Promise<PrivateContextInputs>;
116
- 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
+ }
117
220
 
118
- // TODO(F-335): Exposing the job info is abstraction breakage - drop the following 2 functions.
119
- cycleJob(): Promise<string>;
120
- getCurrentJob(): string;
221
+ function emptyLastCallState(): LastCallState {
222
+ return { offchainEffects: [], queried: false, txHash: Fr.ZERO, anchorBlockTimestamp: 0n };
121
223
  }
122
224
 
123
225
  /**
@@ -127,11 +229,17 @@ export interface TXESessionStateHandler {
127
229
  export class TXESession implements TXESessionStateHandler {
128
230
  private state: SessionState = { name: 'TOP_LEVEL' };
129
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;
130
236
 
131
237
  constructor(
132
238
  private logger: Logger,
239
+ private sessionStore: AztecAsyncKVStore,
133
240
  private stateMachine: TXEStateMachine,
134
241
  private oracleHandler:
242
+ | IMiscOracle
135
243
  | IUtilityExecutionOracle
136
244
  | IPrivateExecutionOracle
137
245
  | IAvmExecutionOracle
@@ -151,11 +259,43 @@ export class TXESession implements TXESessionStateHandler {
151
259
  private chainId: Fr,
152
260
  private version: Fr,
153
261
  private nextBlockTimestamp: bigint,
154
- private contractSyncService: ContractSyncService,
262
+ private readonly artifactResolver: TXEArtifactResolver,
263
+ private readonly rootPath: string,
264
+ private readonly packageName: string,
155
265
  ) {}
156
266
 
157
- static async init(contractStore: ContractStore) {
158
- 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);
159
299
 
160
300
  const addressStore = new AddressStore(store);
161
301
  const privateEventStore = new PrivateEventStore(store);
@@ -167,7 +307,6 @@ export class TXESession implements TXESessionStateHandler {
167
307
  const keyStore = new KeyStore(store);
168
308
  const accountStore = new TXEAccountStore(store);
169
309
 
170
- // Create job coordinator and register staged stores
171
310
  const jobCoordinator = new JobCoordinator(store);
172
311
  jobCoordinator.registerStores([
173
312
  capsuleStore,
@@ -188,7 +327,6 @@ export class TXESession implements TXESessionStateHandler {
188
327
  const initialJobId = jobCoordinator.beginJob();
189
328
 
190
329
  const logger = createLogger('txe:session');
191
- const contractSyncService = new ContractSyncService(stateMachine.node, contractStore, noteStore, logger);
192
330
 
193
331
  const topLevelOracleHandler = new TXEOracleTopLevelContext(
194
332
  stateMachine,
@@ -206,12 +344,16 @@ export class TXESession implements TXESessionStateHandler {
206
344
  version,
207
345
  chainId,
208
346
  new Map(),
209
- contractSyncService,
347
+ artifactResolver,
348
+ rootPath,
349
+ packageName,
210
350
  );
211
- await topLevelOracleHandler.advanceBlocksBy(1);
351
+
352
+ await topLevelOracleHandler.mineDeploymentNullifiers([STANDARD_AUTH_REGISTRY_ADDRESS]);
212
353
 
213
354
  return new TXESession(
214
355
  logger,
356
+ store,
215
357
  stateMachine,
216
358
  topLevelOracleHandler,
217
359
  contractStore,
@@ -229,7 +371,9 @@ export class TXESession implements TXESessionStateHandler {
229
371
  version,
230
372
  chainId,
231
373
  nextBlockTimestamp,
232
- contractSyncService,
374
+ artifactResolver,
375
+ rootPath,
376
+ packageName,
233
377
  );
234
378
  }
235
379
 
@@ -251,7 +395,28 @@ export class TXESession implements TXESessionStateHandler {
251
395
  return translator[validatedFunctionName](...inputs);
252
396
  } catch (error) {
253
397
  if (error instanceof z.ZodError) {
254
- 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}`);
255
420
  } else if (error instanceof Error) {
256
421
  throw new Error(
257
422
  `Execution error while processing function ${functionName} in state ${this.state.name}: ${error.message}`,
@@ -264,17 +429,193 @@ export class TXESession implements TXESessionStateHandler {
264
429
  }
265
430
  }
266
431
 
267
- getCurrentJob(): string {
268
- return this.currentJobId;
269
- }
270
-
271
432
  /** Commits the current job and begins a new one. Returns the new job ID. */
272
- async cycleJob(): Promise<string> {
433
+ private async cycleJob(): Promise<string> {
273
434
  await this.jobCoordinator.commitJob(this.currentJobId);
274
435
  this.currentJobId = this.jobCoordinator.beginJob();
275
436
  return this.currentJobId;
276
437
  }
277
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
+
278
619
  async enterTopLevelState() {
279
620
  switch (this.state.name) {
280
621
  case 'PRIVATE': {
@@ -316,7 +657,9 @@ export class TXESession implements TXESessionStateHandler {
316
657
  this.version,
317
658
  this.chainId,
318
659
  this.authwits,
319
- this.contractSyncService,
660
+ this.artifactResolver,
661
+ this.rootPath,
662
+ this.packageName,
320
663
  );
321
664
 
322
665
  this.state = { name: 'TOP_LEVEL' };
@@ -324,21 +667,25 @@ export class TXESession implements TXESessionStateHandler {
324
667
  }
325
668
 
326
669
  async enterPrivateState(
327
- contractAddress: AztecAddress = DEFAULT_ADDRESS,
328
- anchorBlockNumber?: BlockNumber,
670
+ contractAddressOpt: Option<AztecAddress>,
671
+ anchorBlockNumberOpt: Option<BlockNumber>,
672
+ gasSettings: GasSettings,
329
673
  ): Promise<PrivateContextInputs> {
674
+ const contractAddress = contractAddressOpt?.value ?? DEFAULT_ADDRESS;
675
+ const anchorBlockNumber = anchorBlockNumberOpt?.value;
330
676
  this.exitTopLevelState();
677
+ this.resetLastCall();
331
678
 
332
679
  // Private execution has two associated block numbers: the anchor block (i.e. the historical block that is used to
333
680
  // build the proof), and the *next* block, i.e. the one we'll create once the execution ends, and which will contain
334
681
  // a single transaction with the effects of what was done in the test.
335
- const anchorBlock = await this.stateMachine.node.getBlockHeader(anchorBlockNumber ?? 'latest');
682
+ const anchorBlock = await this.stateMachine.node.getBlock(anchorBlockNumber ?? 'latest').then(b => b?.header);
336
683
 
337
684
  await new NoteService(this.noteStore, this.stateMachine.node, anchorBlock!, this.currentJobId).syncNoteNullifiers(
338
685
  contractAddress,
339
- 'ALL_SCOPES',
686
+ await this.keyStore.getAccounts(),
340
687
  );
341
- const latestBlock = await this.stateMachine.node.getBlockHeader('latest');
688
+ const latestBlock = await this.stateMachine.node.getBlock('latest').then(b => b?.header);
342
689
 
343
690
  const nextBlockGlobalVariables = makeGlobalVariables(undefined, {
344
691
  blockNumber: BlockNumber(latestBlock!.globalVariables.blockNumber + 1),
@@ -353,9 +700,10 @@ export class TXESession implements TXESessionStateHandler {
353
700
  const taggingIndexCache = new ExecutionTaggingIndexCache();
354
701
 
355
702
  const utilityExecutor = this.utilityExecutorForContractSync(anchorBlock);
356
- this.oracleHandler = new PrivateExecutionOracle({
703
+ const transientArrayService = new TransientArrayService();
704
+ this.oracleHandler = new TXEPrivateExecutionOracle({
357
705
  argsHash: Fr.ZERO,
358
- txContext: new TxContext(this.chainId, this.version, GasSettings.empty()),
706
+ txContext: new TxContext(this.chainId, this.version, gasSettings),
359
707
  callContext: new CallContext(AztecAddress.ZERO, contractAddress, FunctionSelector.empty(), false),
360
708
  anchorBlockHeader: anchorBlock!,
361
709
  utilityExecutor,
@@ -372,12 +720,15 @@ export class TXESession implements TXESessionStateHandler {
372
720
  senderTaggingStore: this.senderTaggingStore,
373
721
  recipientTaggingStore: this.recipientTaggingStore,
374
722
  senderAddressBookStore: this.senderAddressBookStore,
375
- capsuleStore: this.capsuleStore,
723
+ capsuleService: new CapsuleService(this.capsuleStore, await this.keyStore.getAccounts()),
376
724
  privateEventStore: this.privateEventStore,
377
725
  contractSyncService: this.stateMachine.contractSyncService,
726
+ l2TipsStore: this.stateMachine.l2TipsProvider,
378
727
  jobId: this.currentJobId,
379
- scopes: 'ALL_SCOPES',
728
+ scopes: await this.keyStore.getAccounts(),
380
729
  messageContextService: this.stateMachine.messageContextService,
730
+ simulator: new WASMSimulator(),
731
+ transientArrayService,
381
732
  });
382
733
 
383
734
  // We store the note and tagging index caches fed into the PrivateExecutionOracle (along with some other auxiliary
@@ -388,35 +739,47 @@ export class TXESession implements TXESessionStateHandler {
388
739
  this.state = { name: 'PRIVATE', nextBlockGlobalVariables, noteCache, taggingIndexCache };
389
740
  this.logger.debug(`Entered state ${this.state.name}`);
390
741
 
391
- 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();
392
747
  }
393
748
 
394
- async enterPublicState(contractAddress?: AztecAddress) {
749
+ async enterPublicState(contractAddressOpt: Option<AztecAddress>) {
750
+ const contractAddress = contractAddressOpt?.value ?? DEFAULT_ADDRESS;
395
751
  this.exitTopLevelState();
752
+ this.resetLastCall();
396
753
 
397
754
  // The PublicContext will create a block with a single transaction in it, containing the effects of what was done in
398
755
  // the test. The block therefore gets the *next* block number and timestamp.
399
- const latestBlockNumber = (await this.stateMachine.node.getBlockHeader('latest'))!.globalVariables.blockNumber;
756
+ const latestHeader = (await this.stateMachine.node.getBlockData('latest'))!.header;
400
757
  const globalVariables = makeGlobalVariables(undefined, {
401
- blockNumber: BlockNumber(latestBlockNumber + 1),
758
+ blockNumber: BlockNumber(latestHeader.globalVariables.blockNumber + 1),
402
759
  timestamp: this.nextBlockTimestamp,
403
760
  version: this.version,
404
761
  chainId: this.chainId,
405
762
  });
406
763
 
407
764
  this.oracleHandler = new TXEOraclePublicContext(
408
- contractAddress ?? DEFAULT_ADDRESS,
765
+ contractAddress,
409
766
  await this.stateMachine.synchronizer.nativeWorldStateService.fork(),
410
767
  getSingleTxBlockRequestHash(globalVariables.blockNumber),
411
768
  globalVariables,
769
+ this.contractStore,
412
770
  );
413
771
 
414
772
  this.state = { name: 'PUBLIC' };
415
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);
416
777
  }
417
778
 
418
- async enterUtilityState(contractAddress: AztecAddress = DEFAULT_ADDRESS) {
779
+ async enterUtilityState(contractAddressOpt: Option<AztecAddress>) {
780
+ const contractAddress = contractAddressOpt?.value ?? DEFAULT_ADDRESS;
419
781
  this.exitTopLevelState();
782
+ this.resetLastCall();
420
783
 
421
784
  const anchorBlockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
422
785
 
@@ -430,7 +793,7 @@ export class TXESession implements TXESessionStateHandler {
430
793
  this.stateMachine.node,
431
794
  anchorBlockHeader,
432
795
  this.currentJobId,
433
- ).syncNoteNullifiers(contractAddress, 'ALL_SCOPES');
796
+ ).syncNoteNullifiers(contractAddress, await this.keyStore.getAccounts());
434
797
 
435
798
  this.oracleHandler = new UtilityExecutionOracle({
436
799
  contractAddress,
@@ -444,16 +807,24 @@ export class TXESession implements TXESessionStateHandler {
444
807
  aztecNode: this.stateMachine.node,
445
808
  recipientTaggingStore: this.recipientTaggingStore,
446
809
  senderAddressBookStore: this.senderAddressBookStore,
447
- capsuleStore: this.capsuleStore,
810
+ capsuleService: new CapsuleService(this.capsuleStore, await this.keyStore.getAccounts()),
448
811
  privateEventStore: this.privateEventStore,
449
812
  messageContextService: this.stateMachine.messageContextService,
450
- contractSyncService: this.contractSyncService,
813
+ contractSyncService: this.stateMachine.contractSyncService,
814
+ l2TipsStore: this.stateMachine.l2TipsProvider,
451
815
  jobId: this.currentJobId,
452
- 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(),
453
821
  });
454
822
 
455
823
  this.state = { name: 'UTILITY' };
456
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);
457
828
  }
458
829
 
459
830
  private exitTopLevelState() {
@@ -518,13 +889,14 @@ export class TXESession implements TXESessionStateHandler {
518
889
  }
519
890
 
520
891
  private utilityExecutorForContractSync(anchorBlock: any) {
521
- return async (call: FunctionCall, scopes: AccessScopes) => {
892
+ return async (call: FunctionCall, scopes: AztecAddress[]) => {
522
893
  const entryPointArtifact = await this.contractStore.getFunctionArtifactWithDebugMetadata(call.to, call.selector);
523
894
  if (entryPointArtifact.functionType !== FunctionType.UTILITY) {
524
895
  throw new Error(`Cannot run ${entryPointArtifact.functionType} function as utility`);
525
896
  }
526
897
 
527
898
  try {
899
+ const simulator = new WASMSimulator();
528
900
  const oracle = new UtilityExecutionOracle({
529
901
  contractAddress: call.to,
530
902
  authWitnesses: [],
@@ -537,15 +909,20 @@ export class TXESession implements TXESessionStateHandler {
537
909
  aztecNode: this.stateMachine.node,
538
910
  recipientTaggingStore: this.recipientTaggingStore,
539
911
  senderAddressBookStore: this.senderAddressBookStore,
540
- capsuleStore: this.capsuleStore,
912
+ capsuleService: new CapsuleService(this.capsuleStore, scopes),
541
913
  privateEventStore: this.privateEventStore,
542
914
  messageContextService: this.stateMachine.messageContextService,
543
- contractSyncService: this.contractSyncService,
915
+ contractSyncService: this.stateMachine.contractSyncService,
916
+ l2TipsStore: this.stateMachine.l2TipsProvider,
544
917
  jobId: this.currentJobId,
545
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(),
546
923
  });
547
- await new WASMSimulator()
548
- .executeUserCircuit(toACVMWitness(0, call.args), entryPointArtifact, new Oracle(oracle).toACIRCallback())
924
+ await simulator
925
+ .executeUserCircuit(toACVMWitness(0, call.args), entryPointArtifact, buildACIRCallback(oracle))
549
926
  .catch((err: Error) => {
550
927
  err.message = resolveAssertionMessageFromError(err, entryPointArtifact);
551
928
  throw new ExecutionError(