@aztec/txe 0.0.1-commit.b2a5d0dd1 → 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 (144) 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 +27 -13
  56. package/dest/oracle/txe_oracle_top_level_context.d.ts.map +1 -1
  57. package/dest/oracle/txe_oracle_top_level_context.js +129 -52
  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 -266
  68. package/dest/rpc_translator.d.ts.map +1 -1
  69. package/dest/rpc_translator.js +699 -730
  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 +24 -14
  75. package/dest/state_machine/dummy_p2p_client.d.ts +11 -6
  76. package/dest/state_machine/dummy_p2p_client.d.ts.map +1 -1
  77. package/dest/state_machine/dummy_p2p_client.js +11 -2
  78. package/dest/state_machine/index.d.ts +4 -2
  79. package/dest/state_machine/index.d.ts.map +1 -1
  80. package/dest/state_machine/index.js +16 -2
  81. package/dest/state_machine/mock_epoch_cache.d.ts +1 -3
  82. package/dest/state_machine/mock_epoch_cache.d.ts.map +1 -1
  83. package/dest/state_machine/mock_epoch_cache.js +0 -6
  84. package/dest/state_machine/synchronizer.js +1 -1
  85. package/dest/txe_session.d.ts +75 -14
  86. package/dest/txe_session.d.ts.map +1 -1
  87. package/dest/txe_session.js +164 -38
  88. package/dest/utils/encoding.d.ts +191 -0
  89. package/dest/utils/encoding.d.ts.map +1 -0
  90. package/dest/{util → utils}/encoding.js +3 -2
  91. package/dest/{util → utils}/expected_failure_error.d.ts +1 -1
  92. package/dest/utils/expected_failure_error.d.ts.map +1 -0
  93. package/dest/{util → utils}/txe_account_store.d.ts +1 -1
  94. package/dest/utils/txe_account_store.d.ts.map +1 -0
  95. package/dest/utils/txe_artifact_resolver.d.ts +37 -0
  96. package/dest/utils/txe_artifact_resolver.d.ts.map +1 -0
  97. package/dest/utils/txe_artifact_resolver.js +161 -0
  98. package/dest/utils/txe_public_contract_data_source.d.ts +20 -0
  99. package/dest/utils/txe_public_contract_data_source.d.ts.map +1 -0
  100. package/dest/worker.bundle.js +3 -0
  101. package/dest/worker.bundle.js.map +7 -0
  102. package/dest/worker.d.ts +2 -0
  103. package/dest/worker.d.ts.map +1 -0
  104. package/dest/worker.js +92 -0
  105. package/package.json +38 -21
  106. package/src/bin/check_txe_oracle_version.ts +70 -0
  107. package/src/bin/index.ts +11 -2
  108. package/src/bin/oracle_test_server.ts +51 -0
  109. package/src/constants.ts +10 -0
  110. package/src/dispatcher_pool.ts +316 -0
  111. package/src/index.ts +97 -227
  112. package/src/msgpackr_fr_extension.ts +23 -0
  113. package/src/oracle/interfaces.ts +29 -7
  114. package/src/oracle/test-resolver/fixtures.ts +84 -0
  115. package/src/oracle/test-resolver/index.ts +45 -0
  116. package/src/oracle/test-resolver/resolver.ts +165 -0
  117. package/src/oracle/txe_oracle_public_context.ts +60 -0
  118. package/src/oracle/txe_oracle_registry.ts +401 -0
  119. package/src/oracle/txe_oracle_top_level_context.ts +157 -62
  120. package/src/oracle/txe_oracle_version.ts +17 -0
  121. package/src/oracle/txe_private_execution_oracle.ts +30 -0
  122. package/src/rpc_server.ts +87 -0
  123. package/src/rpc_translator.ts +774 -1043
  124. package/src/state_machine/archiver.ts +37 -16
  125. package/src/state_machine/dummy_p2p_client.ts +25 -9
  126. package/src/state_machine/index.ts +17 -4
  127. package/src/state_machine/mock_epoch_cache.ts +0 -8
  128. package/src/state_machine/synchronizer.ts +1 -1
  129. package/src/txe_session.ts +269 -39
  130. package/src/{util → utils}/encoding.ts +3 -2
  131. package/src/utils/txe_artifact_resolver.ts +217 -0
  132. package/src/worker.ts +98 -0
  133. package/dest/util/encoding.d.ts +0 -790
  134. package/dest/util/encoding.d.ts.map +0 -1
  135. package/dest/util/expected_failure_error.d.ts.map +0 -1
  136. package/dest/util/txe_account_store.d.ts.map +0 -1
  137. package/dest/util/txe_public_contract_data_source.d.ts +0 -20
  138. package/dest/util/txe_public_contract_data_source.d.ts.map +0 -1
  139. /package/dest/{util → utils}/expected_failure_error.js +0 -0
  140. /package/dest/{util → utils}/txe_account_store.js +0 -0
  141. /package/dest/{util → utils}/txe_public_contract_data_source.js +0 -0
  142. /package/src/{util → utils}/expected_failure_error.ts +0 -0
  143. /package/src/{util → utils}/txe_account_store.ts +0 -0
  144. /package/src/{util → utils}/txe_public_contract_data_source.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';
5
+ import type { AztecAsyncKVStore } from '@aztec/kv-store';
6
+ import { openEphemeralStore } from '@aztec/kv-store/lmdb-v2';
6
7
  import {
7
8
  AddressStore,
8
9
  AnchorBlockStore,
9
10
  CapsuleService,
10
11
  CapsuleStore,
11
12
  ContractStore,
12
- ContractSyncService,
13
13
  JobCoordinator,
14
14
  NoteService,
15
15
  NoteStore,
@@ -22,10 +22,11 @@ import {
22
22
  ExecutionNoteCache,
23
23
  ExecutionTaggingIndexCache,
24
24
  HashedValuesCache,
25
+ type IMiscOracle,
25
26
  type IPrivateExecutionOracle,
26
27
  type IUtilityExecutionOracle,
28
+ Option,
27
29
  Oracle,
28
- PrivateExecutionOracle,
29
30
  UtilityExecutionOracle,
30
31
  } from '@aztec/pxe/simulator';
31
32
  import {
@@ -36,28 +37,32 @@ import {
36
37
  resolveAssertionMessageFromError,
37
38
  toACVMWitness,
38
39
  } from '@aztec/simulator/client';
40
+ import { STANDARD_AUTH_REGISTRY_ADDRESS } from '@aztec/standard-contracts/auth-registry/constants';
39
41
  import { FunctionCall, FunctionSelector, FunctionType } from '@aztec/stdlib/abi';
40
42
  import type { AuthWitness } from '@aztec/stdlib/auth-witness';
41
43
  import { AztecAddress } from '@aztec/stdlib/aztec-address';
42
- import { GasSettings } from '@aztec/stdlib/gas';
44
+ import type { GasSettings } from '@aztec/stdlib/gas';
43
45
  import { computeProtocolNullifier } from '@aztec/stdlib/hash';
44
46
  import { PrivateContextInputs } from '@aztec/stdlib/kernel';
45
47
  import { makeGlobalVariables } from '@aztec/stdlib/testing';
46
- import { CallContext, GlobalVariables, TxContext } from '@aztec/stdlib/tx';
48
+ import { CallContext, GlobalVariables, OFFCHAIN_MESSAGE_IDENTIFIER, TxContext } from '@aztec/stdlib/tx';
47
49
 
48
50
  import { z } from 'zod';
49
51
 
50
- import { DEFAULT_ADDRESS } from './constants.js';
52
+ import { DEFAULT_ADDRESS, MAX_OFFCHAIN_EFFECTS_PER_TXE_QUERY, MAX_OFFCHAIN_EFFECT_LEN } from './constants.js';
51
53
  import type { IAvmExecutionOracle, ITxeExecutionOracle } from './oracle/interfaces.js';
52
54
  import { TXEOraclePublicContext } from './oracle/txe_oracle_public_context.js';
53
55
  import { TXEOracleTopLevelContext } from './oracle/txe_oracle_top_level_context.js';
56
+ import { TXE_ORACLE_VERSION_MAJOR, TXE_ORACLE_VERSION_MINOR } from './oracle/txe_oracle_version.js';
57
+ import { TXEPrivateExecutionOracle } from './oracle/txe_private_execution_oracle.js';
54
58
  import { RPCTranslator } from './rpc_translator.js';
55
59
  import { TXEArchiver } from './state_machine/archiver.js';
56
60
  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
61
  import { getSingleTxBlockRequestHash, insertTxEffectIntoWorldTrees, makeTXEBlock } from './utils/block_creation.js';
62
+ import type { ForeignCallArgs, ForeignCallResult } from './utils/encoding.js';
60
63
  import { makeTxEffect } from './utils/tx_effect_creation.js';
64
+ import { TXEAccountStore } from './utils/txe_account_store.js';
65
+ import type { TXEArtifactResolver } from './utils/txe_artifact_resolver.js';
61
66
 
62
67
  /**
63
68
  * A TXE Session can be in one of four states, which change as the test progresses and different oracles are called.
@@ -110,14 +115,80 @@ export type TXEOracleFunctionName = Exclude<
110
115
  >;
111
116
 
112
117
  export interface TXESessionStateHandler {
118
+ /** Records the TXE oracle version reported by the Noir test code for diagnostics. */
119
+ setTxeOracleVersion(major: number, minor: number): void;
120
+
113
121
  enterTopLevelState(): Promise<void>;
114
- enterPublicState(contractAddress?: AztecAddress): Promise<void>;
115
- enterPrivateState(contractAddress?: AztecAddress, anchorBlockNumber?: BlockNumber): Promise<PrivateContextInputs>;
116
- enterUtilityState(contractAddress?: AztecAddress): Promise<void>;
122
+ enterPublicState(contractAddress: Option<AztecAddress>): Promise<void>;
123
+ enterPrivateState(
124
+ contractAddress: Option<AztecAddress>,
125
+ anchorBlockNumber: Option<BlockNumber>,
126
+ gasSettings: GasSettings,
127
+ ): Promise<PrivateContextInputs>;
128
+ enterUtilityState(contractAddress: Option<AztecAddress>): Promise<void>;
117
129
 
118
130
  // TODO(F-335): Exposing the job info is abstraction breakage - drop the following 2 functions.
119
131
  cycleJob(): Promise<string>;
120
132
  getCurrentJob(): string;
133
+
134
+ /**
135
+ * Runs an executor-style top-level call (private/public call, utility execution) with last-call tracking.
136
+ */
137
+ withTopLevelCallTracking<T>(work: () => Promise<{ result: T; txHash?: Fr }>): Promise<T>;
138
+
139
+ /**
140
+ * Captures a raw offchain effect payload for consumption from test environment. Called by the `emit_offchain_effect`
141
+ * oracle handler whenever a contract function emits an offchain message, at any call depth.
142
+ */
143
+ recordOffchainEffect(data: Fr[]): void;
144
+
145
+ /**
146
+ * Returns the raw offchain effect payloads emitted by the last top-level call. Each payload follows the protocol
147
+ * convention documented on `OFFCHAIN_MESSAGE_IDENTIFIER`, i.e. `[identifier, recipient, ...ciphertext]`. Decoding into
148
+ * `OffchainMessage` structs happens on the Noir side of the test helper. Marks the buffer as queried so the
149
+ * unqueried-messages warning doesn't fire on the next reset.
150
+ */
151
+ getLastCallOffchainEffects(): { effects: Fr[][] };
152
+
153
+ /**
154
+ * Returns the context of the last top-level call: its tx hash (`Fr.ZERO` if the call was tx-less) and the anchor
155
+ * block timestamp captured at the start of the call. Does *not* mark the buffer as queried — context reads are
156
+ * metadata, not effect consumption.
157
+ */
158
+ getLastCallContext(): { txHash: Fr; anchorBlockTimestamp: bigint };
159
+ }
160
+
161
+ /**
162
+ * Session state tracking the most recently completed top-level call: the offchain effect buffer it produced, and the
163
+ * call's context (tx hash + anchor block timestamp). The context is refreshed on every top-level call, independently
164
+ * of whether the call produced offchain effects.
165
+ */
166
+ interface LastCallState {
167
+ /**
168
+ * Raw offchain effect payloads emitted by the currently-executing (or most recently completed) top-level call. Wiped
169
+ * at the start of every top-level entry point, appended to on every `emit_offchain_effect` oracle invocation.
170
+ */
171
+ offchainEffects: Fr[][];
172
+ /**
173
+ * Tracks whether the test has queried `effects` since the last reset. If a new top-level call clobbers the buffer
174
+ * without it being queried first, any accumulated messages are lost and we emit a warning so tests don't silently
175
+ * drop delivery.
176
+ */
177
+ queried: boolean;
178
+ /**
179
+ * Tx hash of the most recently completed top-level call, or `Fr.ZERO` if the call was tx-less (context setters,
180
+ * utility execution). Populated by call executor handlers after execution completes.
181
+ */
182
+ txHash: Fr;
183
+ /**
184
+ * Anchor block timestamp of the most recently completed top-level call, captured from the anchor block header that
185
+ * was active when the call started. Populated by call executor handlers after execution completes.
186
+ */
187
+ anchorBlockTimestamp: bigint;
188
+ }
189
+
190
+ function emptyLastCallState(): LastCallState {
191
+ return { offchainEffects: [], queried: false, txHash: Fr.ZERO, anchorBlockTimestamp: 0n };
121
192
  }
122
193
 
123
194
  /**
@@ -127,11 +198,17 @@ export interface TXESessionStateHandler {
127
198
  export class TXESession implements TXESessionStateHandler {
128
199
  private state: SessionState = { name: 'TOP_LEVEL' };
129
200
  private authwits: Map<string, AuthWitness> = new Map();
201
+ private lastCallInfo: LastCallState = emptyLastCallState();
202
+ private txeOracleVersion: { major: number; minor: number } | undefined;
203
+
204
+ private disposed = false;
130
205
 
131
206
  constructor(
132
207
  private logger: Logger,
208
+ private sessionStore: AztecAsyncKVStore,
133
209
  private stateMachine: TXEStateMachine,
134
210
  private oracleHandler:
211
+ | IMiscOracle
135
212
  | IUtilityExecutionOracle
136
213
  | IPrivateExecutionOracle
137
214
  | IAvmExecutionOracle
@@ -151,11 +228,43 @@ export class TXESession implements TXESessionStateHandler {
151
228
  private chainId: Fr,
152
229
  private version: Fr,
153
230
  private nextBlockTimestamp: bigint,
154
- private contractSyncService: ContractSyncService,
231
+ private readonly artifactResolver: TXEArtifactResolver,
232
+ private readonly rootPath: string,
233
+ private readonly packageName: string,
155
234
  ) {}
156
235
 
157
- static async init(contractStore: ContractStore) {
158
- const store = await openTmpStore('txe-session');
236
+ /**
237
+ * Closes the per-session `txe-session` LMDB and the `NativeWorldStateService` .
238
+ * Called via IPC when the dispatcher detects the end of a test. Idempotent.
239
+ */
240
+ async dispose(): Promise<void> {
241
+ if (this.disposed) {
242
+ return;
243
+ }
244
+ this.disposed = true;
245
+ try {
246
+ await this.stateMachine.synchronizer.nativeWorldStateService.close();
247
+ } catch (err) {
248
+ this.logger.warn(`Error closing native world state during session dispose`, err);
249
+ }
250
+ try {
251
+ await this.sessionStore.close();
252
+ } catch (err) {
253
+ this.logger.warn(`Error closing session LMDB during dispose`, err);
254
+ }
255
+ }
256
+
257
+ static async init(
258
+ contractStore: ContractStore,
259
+ artifactResolver: TXEArtifactResolver,
260
+ rootPath: string,
261
+ packageName: string,
262
+ ) {
263
+ // Size LMDB's reader slots to the libuv pool (capped to 2 in bin/index.ts via
264
+ // HARDWARE_CONCURRENCY): each native LMDB read needs a libuv worker thread to run, so any
265
+ // slot beyond the pool size would sit idle while still consuming a semaphore + reader-table
266
+ // entry per session.
267
+ const store = await openEphemeralStore('txe-session', undefined, 2);
159
268
 
160
269
  const addressStore = new AddressStore(store);
161
270
  const privateEventStore = new PrivateEventStore(store);
@@ -167,7 +276,6 @@ export class TXESession implements TXESessionStateHandler {
167
276
  const keyStore = new KeyStore(store);
168
277
  const accountStore = new TXEAccountStore(store);
169
278
 
170
- // Create job coordinator and register staged stores
171
279
  const jobCoordinator = new JobCoordinator(store);
172
280
  jobCoordinator.registerStores([
173
281
  capsuleStore,
@@ -188,7 +296,6 @@ export class TXESession implements TXESessionStateHandler {
188
296
  const initialJobId = jobCoordinator.beginJob();
189
297
 
190
298
  const logger = createLogger('txe:session');
191
- const contractSyncService = new ContractSyncService(stateMachine.node, contractStore, noteStore, logger);
192
299
 
193
300
  const topLevelOracleHandler = new TXEOracleTopLevelContext(
194
301
  stateMachine,
@@ -206,12 +313,16 @@ export class TXESession implements TXESessionStateHandler {
206
313
  version,
207
314
  chainId,
208
315
  new Map(),
209
- contractSyncService,
316
+ artifactResolver,
317
+ rootPath,
318
+ packageName,
210
319
  );
211
- await topLevelOracleHandler.advanceBlocksBy(1);
320
+
321
+ await topLevelOracleHandler.mineDeploymentNullifiers([STANDARD_AUTH_REGISTRY_ADDRESS]);
212
322
 
213
323
  return new TXESession(
214
324
  logger,
325
+ store,
215
326
  stateMachine,
216
327
  topLevelOracleHandler,
217
328
  contractStore,
@@ -229,7 +340,9 @@ export class TXESession implements TXESessionStateHandler {
229
340
  version,
230
341
  chainId,
231
342
  nextBlockTimestamp,
232
- contractSyncService,
343
+ artifactResolver,
344
+ rootPath,
345
+ packageName,
233
346
  );
234
347
  }
235
348
 
@@ -251,7 +364,28 @@ export class TXESession implements TXESessionStateHandler {
251
364
  return translator[validatedFunctionName](...inputs);
252
365
  } catch (error) {
253
366
  if (error instanceof z.ZodError) {
254
- throw new Error(`${functionName} does not correspond to any oracle handler available on RPCTranslator`);
367
+ let versionHint: string;
368
+ if (!this.txeOracleVersion) {
369
+ versionHint =
370
+ ' The test appears to use an older version of Aztec.nr that does not' +
371
+ ' support test environment oracle versioning. Update Aztec.nr to a compatible version.' +
372
+ ' See https://docs.aztec.network/errors/12';
373
+ } else if (this.txeOracleVersion.minor > TXE_ORACLE_VERSION_MINOR) {
374
+ versionHint =
375
+ ` The test uses Aztec.nr test oracle version` +
376
+ ` ${this.txeOracleVersion.major}.${this.txeOracleVersion.minor}, but this test environment` +
377
+ ` only supports up to ${TXE_ORACLE_VERSION_MAJOR}.${TXE_ORACLE_VERSION_MINOR}.` +
378
+ ` Upgrade the Aztec CLI to a compatible version.` +
379
+ ` See https://docs.aztec.network/errors/12`;
380
+ } else {
381
+ versionHint =
382
+ ` The test's oracle version (${this.txeOracleVersion.major}.${this.txeOracleVersion.minor})` +
383
+ ` is compatible with this test environment` +
384
+ ` (${TXE_ORACLE_VERSION_MAJOR}.${TXE_ORACLE_VERSION_MINOR}), so this oracle should be` +
385
+ ` available. This is an unexpected error, please report it.` +
386
+ ` See https://docs.aztec.network/errors/13`;
387
+ }
388
+ throw new Error(`Unknown oracle '${functionName}'.${versionHint}`);
255
389
  } else if (error instanceof Error) {
256
390
  throw new Error(
257
391
  `Execution error while processing function ${functionName} in state ${this.state.name}: ${error.message}`,
@@ -275,6 +409,75 @@ export class TXESession implements TXESessionStateHandler {
275
409
  return this.currentJobId;
276
410
  }
277
411
 
412
+ private resetLastCall(): void {
413
+ const notQueriedMessageCount = this.lastCallInfo.queried
414
+ ? 0
415
+ : this.lastCallInfo.offchainEffects.filter(payload => payload[0]?.equals(OFFCHAIN_MESSAGE_IDENTIFIER)).length;
416
+ if (notQueriedMessageCount > 0) {
417
+ this.logger.warn(
418
+ `Dropping ${notQueriedMessageCount} unqueried offchain message(s) from the previous top-level call. ` +
419
+ `To deliver them, call \`env.offchain_messages()\` and forward the result to the recipient contract's ` +
420
+ `\`offchain_receive\` utility before issuing another top-level call. To intentionally discard, assign ` +
421
+ `to \`let _ = env.offchain_messages()\` to silence this warning.`,
422
+ );
423
+ }
424
+ this.lastCallInfo = emptyLastCallState();
425
+ }
426
+
427
+ recordOffchainEffect(data: Fr[]): void {
428
+ this.lastCallInfo.offchainEffects.push(data);
429
+ }
430
+
431
+ private setLastCallContext(txHash: Fr, anchorBlockTimestamp: bigint): void {
432
+ this.lastCallInfo.txHash = txHash;
433
+ this.lastCallInfo.anchorBlockTimestamp = anchorBlockTimestamp;
434
+ }
435
+
436
+ async withTopLevelCallTracking<T>(work: () => Promise<{ result: T; txHash?: Fr }>): Promise<T> {
437
+ this.resetLastCall();
438
+ // Capture the anchor *before* `work` runs: private/public executor calls mine a new block as a
439
+ // side effect, and that block's timestamp should not be attributed to this call's anchor.
440
+ const anchorBlockTimestamp = (await this.stateMachine.node.getBlockData('latest'))!.header.globalVariables
441
+ .timestamp;
442
+ const { result, txHash } = await work();
443
+ this.setLastCallContext(txHash ?? Fr.ZERO, anchorBlockTimestamp);
444
+ return result;
445
+ }
446
+
447
+ getLastCallOffchainEffects(): { effects: Fr[][] } {
448
+ this.lastCallInfo.queried = true;
449
+ const effects = this.lastCallInfo.offchainEffects;
450
+
451
+ if (effects.length > MAX_OFFCHAIN_EFFECTS_PER_TXE_QUERY) {
452
+ throw new Error(`${effects.length} offchain effects exceed max ${MAX_OFFCHAIN_EFFECTS_PER_TXE_QUERY}`);
453
+ }
454
+ if (effects.some(e => e.length > MAX_OFFCHAIN_EFFECT_LEN)) {
455
+ throw new Error(`Some offchain effect has length larger than max ${MAX_OFFCHAIN_EFFECT_LEN}`);
456
+ }
457
+
458
+ return { effects };
459
+ }
460
+
461
+ getLastCallContext(): { txHash: Fr; anchorBlockTimestamp: bigint } {
462
+ const { txHash, anchorBlockTimestamp } = this.lastCallInfo;
463
+ return { txHash, anchorBlockTimestamp };
464
+ }
465
+
466
+ setTxeOracleVersion(major: number, minor: number): void {
467
+ if (major !== TXE_ORACLE_VERSION_MAJOR) {
468
+ const hint =
469
+ major > TXE_ORACLE_VERSION_MAJOR
470
+ ? 'The test was compiled with a newer version of Aztec.nr than your test environment supports. Upgrade your test environment to a compatible version.'
471
+ : '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.';
472
+ throw new Error(
473
+ `Incompatible test environment version: ${hint} See https://docs.aztec.network/errors/12 (expected test oracle major version ${TXE_ORACLE_VERSION_MAJOR}, got ${major})`,
474
+ );
475
+ }
476
+
477
+ this.txeOracleVersion = { major, minor };
478
+ this.logger.debug(`Test compiled with test oracle version ${major}.${minor}`);
479
+ }
480
+
278
481
  async enterTopLevelState() {
279
482
  switch (this.state.name) {
280
483
  case 'PRIVATE': {
@@ -316,7 +519,9 @@ export class TXESession implements TXESessionStateHandler {
316
519
  this.version,
317
520
  this.chainId,
318
521
  this.authwits,
319
- this.contractSyncService,
522
+ this.artifactResolver,
523
+ this.rootPath,
524
+ this.packageName,
320
525
  );
321
526
 
322
527
  this.state = { name: 'TOP_LEVEL' };
@@ -324,21 +529,25 @@ export class TXESession implements TXESessionStateHandler {
324
529
  }
325
530
 
326
531
  async enterPrivateState(
327
- contractAddress: AztecAddress = DEFAULT_ADDRESS,
328
- anchorBlockNumber?: BlockNumber,
532
+ contractAddressOpt: Option<AztecAddress>,
533
+ anchorBlockNumberOpt: Option<BlockNumber>,
534
+ gasSettings: GasSettings,
329
535
  ): Promise<PrivateContextInputs> {
536
+ const contractAddress = contractAddressOpt?.value ?? DEFAULT_ADDRESS;
537
+ const anchorBlockNumber = anchorBlockNumberOpt?.value;
330
538
  this.exitTopLevelState();
539
+ this.resetLastCall();
331
540
 
332
541
  // Private execution has two associated block numbers: the anchor block (i.e. the historical block that is used to
333
542
  // build the proof), and the *next* block, i.e. the one we'll create once the execution ends, and which will contain
334
543
  // a single transaction with the effects of what was done in the test.
335
- const anchorBlock = await this.stateMachine.node.getBlockHeader(anchorBlockNumber ?? 'latest');
544
+ const anchorBlock = await this.stateMachine.node.getBlock(anchorBlockNumber ?? 'latest').then(b => b?.header);
336
545
 
337
546
  await new NoteService(this.noteStore, this.stateMachine.node, anchorBlock!, this.currentJobId).syncNoteNullifiers(
338
547
  contractAddress,
339
548
  await this.keyStore.getAccounts(),
340
549
  );
341
- const latestBlock = await this.stateMachine.node.getBlockHeader('latest');
550
+ const latestBlock = await this.stateMachine.node.getBlock('latest').then(b => b?.header);
342
551
 
343
552
  const nextBlockGlobalVariables = makeGlobalVariables(undefined, {
344
553
  blockNumber: BlockNumber(latestBlock!.globalVariables.blockNumber + 1),
@@ -353,9 +562,9 @@ export class TXESession implements TXESessionStateHandler {
353
562
  const taggingIndexCache = new ExecutionTaggingIndexCache();
354
563
 
355
564
  const utilityExecutor = this.utilityExecutorForContractSync(anchorBlock);
356
- this.oracleHandler = new PrivateExecutionOracle({
565
+ this.oracleHandler = new TXEPrivateExecutionOracle({
357
566
  argsHash: Fr.ZERO,
358
- txContext: new TxContext(this.chainId, this.version, GasSettings.empty()),
567
+ txContext: new TxContext(this.chainId, this.version, gasSettings),
359
568
  callContext: new CallContext(AztecAddress.ZERO, contractAddress, FunctionSelector.empty(), false),
360
569
  anchorBlockHeader: anchorBlock!,
361
570
  utilityExecutor,
@@ -375,10 +584,11 @@ export class TXESession implements TXESessionStateHandler {
375
584
  capsuleService: new CapsuleService(this.capsuleStore, await this.keyStore.getAccounts()),
376
585
  privateEventStore: this.privateEventStore,
377
586
  contractSyncService: this.stateMachine.contractSyncService,
378
- l2TipsStore: this.stateMachine.node,
587
+ l2TipsStore: this.stateMachine.l2TipsProvider,
379
588
  jobId: this.currentJobId,
380
589
  scopes: await this.keyStore.getAccounts(),
381
590
  messageContextService: this.stateMachine.messageContextService,
591
+ simulator: new WASMSimulator(),
382
592
  });
383
593
 
384
594
  // We store the note and tagging index caches fed into the PrivateExecutionOracle (along with some other auxiliary
@@ -389,35 +599,47 @@ export class TXESession implements TXESessionStateHandler {
389
599
  this.state = { name: 'PRIVATE', nextBlockGlobalVariables, noteCache, taggingIndexCache };
390
600
  this.logger.debug(`Entered state ${this.state.name}`);
391
601
 
392
- return (this.oracleHandler as PrivateExecutionOracle).getPrivateContextInputs();
602
+ // Record the *resolved* anchor's timestamp — if the caller pinned the anchor to a past block
603
+ // via `anchorBlockNumber`, "latest" would be the wrong anchor for offchain-message semantics.
604
+ this.setLastCallContext(Fr.ZERO, anchorBlock!.globalVariables.timestamp);
605
+
606
+ return (this.oracleHandler as TXEPrivateExecutionOracle).getPrivateContextInputs();
393
607
  }
394
608
 
395
- async enterPublicState(contractAddress?: AztecAddress) {
609
+ async enterPublicState(contractAddressOpt: Option<AztecAddress>) {
610
+ const contractAddress = contractAddressOpt?.value ?? DEFAULT_ADDRESS;
396
611
  this.exitTopLevelState();
612
+ this.resetLastCall();
397
613
 
398
614
  // The PublicContext will create a block with a single transaction in it, containing the effects of what was done in
399
615
  // the test. The block therefore gets the *next* block number and timestamp.
400
- const latestBlockNumber = (await this.stateMachine.node.getBlockHeader('latest'))!.globalVariables.blockNumber;
616
+ const latestHeader = (await this.stateMachine.node.getBlockData('latest'))!.header;
401
617
  const globalVariables = makeGlobalVariables(undefined, {
402
- blockNumber: BlockNumber(latestBlockNumber + 1),
618
+ blockNumber: BlockNumber(latestHeader.globalVariables.blockNumber + 1),
403
619
  timestamp: this.nextBlockTimestamp,
404
620
  version: this.version,
405
621
  chainId: this.chainId,
406
622
  });
407
623
 
408
624
  this.oracleHandler = new TXEOraclePublicContext(
409
- contractAddress ?? DEFAULT_ADDRESS,
625
+ contractAddress,
410
626
  await this.stateMachine.synchronizer.nativeWorldStateService.fork(),
411
627
  getSingleTxBlockRequestHash(globalVariables.blockNumber),
412
628
  globalVariables,
629
+ this.contractStore,
413
630
  );
414
631
 
415
632
  this.state = { name: 'PUBLIC' };
416
633
  this.logger.debug(`Entered state ${this.state.name}`);
634
+
635
+ // Public state is anchored at the latest block.
636
+ this.setLastCallContext(Fr.ZERO, latestHeader.globalVariables.timestamp);
417
637
  }
418
638
 
419
- async enterUtilityState(contractAddress: AztecAddress = DEFAULT_ADDRESS) {
639
+ async enterUtilityState(contractAddressOpt: Option<AztecAddress>) {
640
+ const contractAddress = contractAddressOpt?.value ?? DEFAULT_ADDRESS;
420
641
  this.exitTopLevelState();
642
+ this.resetLastCall();
421
643
 
422
644
  const anchorBlockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
423
645
 
@@ -448,14 +670,19 @@ export class TXESession implements TXESessionStateHandler {
448
670
  capsuleService: new CapsuleService(this.capsuleStore, await this.keyStore.getAccounts()),
449
671
  privateEventStore: this.privateEventStore,
450
672
  messageContextService: this.stateMachine.messageContextService,
451
- contractSyncService: this.contractSyncService,
452
- l2TipsStore: this.stateMachine.node,
673
+ contractSyncService: this.stateMachine.contractSyncService,
674
+ l2TipsStore: this.stateMachine.l2TipsProvider,
453
675
  jobId: this.currentJobId,
454
676
  scopes: await this.keyStore.getAccounts(),
677
+ simulator: new WASMSimulator(),
678
+ utilityExecutor: this.utilityExecutorForContractSync(anchorBlockHeader),
455
679
  });
456
680
 
457
681
  this.state = { name: 'UTILITY' };
458
682
  this.logger.debug(`Entered state ${this.state.name}`);
683
+
684
+ // Utility state anchors at whatever the anchor block store is pointing to (tracked as latest).
685
+ this.setLastCallContext(Fr.ZERO, anchorBlockHeader.globalVariables.timestamp);
459
686
  }
460
687
 
461
688
  private exitTopLevelState() {
@@ -527,6 +754,7 @@ export class TXESession implements TXESessionStateHandler {
527
754
  }
528
755
 
529
756
  try {
757
+ const simulator = new WASMSimulator();
530
758
  const oracle = new UtilityExecutionOracle({
531
759
  contractAddress: call.to,
532
760
  authWitnesses: [],
@@ -542,12 +770,14 @@ export class TXESession implements TXESessionStateHandler {
542
770
  capsuleService: new CapsuleService(this.capsuleStore, scopes),
543
771
  privateEventStore: this.privateEventStore,
544
772
  messageContextService: this.stateMachine.messageContextService,
545
- contractSyncService: this.contractSyncService,
546
- l2TipsStore: this.stateMachine.node,
773
+ contractSyncService: this.stateMachine.contractSyncService,
774
+ l2TipsStore: this.stateMachine.l2TipsProvider,
547
775
  jobId: this.currentJobId,
548
776
  scopes,
777
+ simulator,
778
+ utilityExecutor: this.utilityExecutorForContractSync(anchorBlock),
549
779
  });
550
- await new WASMSimulator()
780
+ await simulator
551
781
  .executeUserCircuit(toACVMWitness(0, call.args), entryPointArtifact, new Oracle(oracle).toACIRCallback())
552
782
  .catch((err: Error) => {
553
783
  err.message = resolveAssertionMessageFromError(err, entryPointArtifact);
@@ -147,9 +147,10 @@ export function arrayOfArraysToBoundedVecOfArrays(
147
147
 
148
148
  const numFieldsToPad = maxLen * nestedArrayLength - flattenedStorage.length;
149
149
 
150
- const flattenedStorageWithPadding = flattenedStorage.concat(Array(numFieldsToPad).fill(new Fr(0)));
150
+ // Pad with hex-encoded zeros so the result is uniformly `ForeignCallSingle` (string); raw Fr
151
+ // instances would JSON-serialize as `{ asBigInt: "0" }` and nargo would reject the response.
152
+ const flattenedStorageWithPadding = flattenedStorage.concat(toArray(Array(numFieldsToPad).fill(new Fr(0))));
151
153
 
152
- // At last we get the actual length of the BoundedVec and return the values.
153
154
  const len = toSingle(new Fr(bVecStorage.length));
154
155
  return [flattenedStorageWithPadding, len];
155
156
  }