@aztec/txe 0.0.1-commit.b6e433891 → 0.0.1-commit.b9865e97

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 (148) 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-BJVAAXNA.js +3 -0
  20. package/dest/chunk-BJVAAXNA.js.map +7 -0
  21. package/dest/chunk-CUSVOJA4.js +304 -0
  22. package/dest/chunk-CUSVOJA4.js.map +7 -0
  23. package/dest/chunk-OYLIPWI6.js +260 -0
  24. package/dest/chunk-OYLIPWI6.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 +36863 -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 +33 -14
  56. package/dest/oracle/txe_oracle_top_level_context.d.ts.map +1 -1
  57. package/dest/oracle/txe_oracle_top_level_context.js +149 -60
  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 -233
  68. package/dest/rpc_translator.d.ts.map +1 -1
  69. package/dest/rpc_translator.js +706 -637
  70. package/dest/server.bundle.js +3 -0
  71. package/dest/server.bundle.js.map +7 -0
  72. package/dest/state_machine/archiver.d.ts +4 -3
  73. package/dest/state_machine/archiver.d.ts.map +1 -1
  74. package/dest/state_machine/archiver.js +26 -15
  75. package/dest/state_machine/dummy_p2p_client.d.ts +13 -7
  76. package/dest/state_machine/dummy_p2p_client.d.ts.map +1 -1
  77. package/dest/state_machine/dummy_p2p_client.js +16 -4
  78. package/dest/state_machine/global_variable_builder.d.ts +9 -4
  79. package/dest/state_machine/global_variable_builder.d.ts.map +1 -1
  80. package/dest/state_machine/global_variable_builder.js +9 -3
  81. package/dest/state_machine/index.d.ts +4 -2
  82. package/dest/state_machine/index.d.ts.map +1 -1
  83. package/dest/state_machine/index.js +17 -3
  84. package/dest/state_machine/mock_epoch_cache.d.ts +1 -2
  85. package/dest/state_machine/mock_epoch_cache.d.ts.map +1 -1
  86. package/dest/state_machine/mock_epoch_cache.js +0 -3
  87. package/dest/state_machine/synchronizer.js +1 -1
  88. package/dest/txe_session.d.ts +75 -14
  89. package/dest/txe_session.d.ts.map +1 -1
  90. package/dest/txe_session.js +170 -41
  91. package/dest/utils/encoding.d.ts +191 -0
  92. package/dest/utils/encoding.d.ts.map +1 -0
  93. package/dest/{util → utils}/encoding.js +7 -2
  94. package/dest/{util → utils}/expected_failure_error.d.ts +1 -1
  95. package/dest/utils/expected_failure_error.d.ts.map +1 -0
  96. package/dest/{util → utils}/txe_account_store.d.ts +1 -1
  97. package/dest/utils/txe_account_store.d.ts.map +1 -0
  98. package/dest/utils/txe_artifact_resolver.d.ts +37 -0
  99. package/dest/utils/txe_artifact_resolver.d.ts.map +1 -0
  100. package/dest/utils/txe_artifact_resolver.js +161 -0
  101. package/dest/utils/txe_public_contract_data_source.d.ts +20 -0
  102. package/dest/utils/txe_public_contract_data_source.d.ts.map +1 -0
  103. package/dest/{util → utils}/txe_public_contract_data_source.js +1 -3
  104. package/dest/worker.bundle.js +3 -0
  105. package/dest/worker.bundle.js.map +7 -0
  106. package/dest/worker.d.ts +2 -0
  107. package/dest/worker.d.ts.map +1 -0
  108. package/dest/worker.js +92 -0
  109. package/package.json +38 -21
  110. package/src/bin/check_txe_oracle_version.ts +70 -0
  111. package/src/bin/index.ts +11 -2
  112. package/src/bin/oracle_test_server.ts +51 -0
  113. package/src/constants.ts +10 -0
  114. package/src/dispatcher_pool.ts +316 -0
  115. package/src/index.ts +97 -227
  116. package/src/msgpackr_fr_extension.ts +23 -0
  117. package/src/oracle/interfaces.ts +29 -7
  118. package/src/oracle/test-resolver/fixtures.ts +84 -0
  119. package/src/oracle/test-resolver/index.ts +45 -0
  120. package/src/oracle/test-resolver/resolver.ts +165 -0
  121. package/src/oracle/txe_oracle_public_context.ts +60 -0
  122. package/src/oracle/txe_oracle_registry.ts +401 -0
  123. package/src/oracle/txe_oracle_top_level_context.ts +180 -67
  124. package/src/oracle/txe_oracle_version.ts +17 -0
  125. package/src/oracle/txe_private_execution_oracle.ts +30 -0
  126. package/src/rpc_server.ts +87 -0
  127. package/src/rpc_translator.ts +787 -925
  128. package/src/state_machine/archiver.ts +38 -16
  129. package/src/state_machine/dummy_p2p_client.ts +31 -11
  130. package/src/state_machine/global_variable_builder.ts +18 -3
  131. package/src/state_machine/index.ts +20 -5
  132. package/src/state_machine/mock_epoch_cache.ts +0 -4
  133. package/src/state_machine/synchronizer.ts +1 -1
  134. package/src/txe_session.ts +278 -45
  135. package/src/{util → utils}/encoding.ts +8 -2
  136. package/src/utils/txe_artifact_resolver.ts +217 -0
  137. package/src/{util → utils}/txe_public_contract_data_source.ts +0 -2
  138. package/src/worker.ts +98 -0
  139. package/dest/util/encoding.d.ts +0 -720
  140. package/dest/util/encoding.d.ts.map +0 -1
  141. package/dest/util/expected_failure_error.d.ts.map +0 -1
  142. package/dest/util/txe_account_store.d.ts.map +0 -1
  143. package/dest/util/txe_public_contract_data_source.d.ts +0 -20
  144. package/dest/util/txe_public_contract_data_source.d.ts.map +0 -1
  145. /package/dest/{util → utils}/expected_failure_error.js +0 -0
  146. /package/dest/{util → utils}/txe_account_store.js +0 -0
  147. /package/src/{util → utils}/expected_failure_error.ts +0 -0
  148. /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,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
- 'ALL_SCOPES',
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,
@@ -372,12 +581,14 @@ export class TXESession implements TXESessionStateHandler {
372
581
  senderTaggingStore: this.senderTaggingStore,
373
582
  recipientTaggingStore: this.recipientTaggingStore,
374
583
  senderAddressBookStore: this.senderAddressBookStore,
375
- capsuleStore: this.capsuleStore,
584
+ capsuleService: new CapsuleService(this.capsuleStore, await this.keyStore.getAccounts()),
376
585
  privateEventStore: this.privateEventStore,
377
586
  contractSyncService: this.stateMachine.contractSyncService,
587
+ l2TipsStore: this.stateMachine.l2TipsProvider,
378
588
  jobId: this.currentJobId,
379
- scopes: 'ALL_SCOPES',
589
+ scopes: await this.keyStore.getAccounts(),
380
590
  messageContextService: this.stateMachine.messageContextService,
591
+ simulator: new WASMSimulator(),
381
592
  });
382
593
 
383
594
  // We store the note and tagging index caches fed into the PrivateExecutionOracle (along with some other auxiliary
@@ -388,35 +599,47 @@ export class TXESession implements TXESessionStateHandler {
388
599
  this.state = { name: 'PRIVATE', nextBlockGlobalVariables, noteCache, taggingIndexCache };
389
600
  this.logger.debug(`Entered state ${this.state.name}`);
390
601
 
391
- 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();
392
607
  }
393
608
 
394
- async enterPublicState(contractAddress?: AztecAddress) {
609
+ async enterPublicState(contractAddressOpt: Option<AztecAddress>) {
610
+ const contractAddress = contractAddressOpt?.value ?? DEFAULT_ADDRESS;
395
611
  this.exitTopLevelState();
612
+ this.resetLastCall();
396
613
 
397
614
  // The PublicContext will create a block with a single transaction in it, containing the effects of what was done in
398
615
  // the test. The block therefore gets the *next* block number and timestamp.
399
- const latestBlockNumber = (await this.stateMachine.node.getBlockHeader('latest'))!.globalVariables.blockNumber;
616
+ const latestHeader = (await this.stateMachine.node.getBlockData('latest'))!.header;
400
617
  const globalVariables = makeGlobalVariables(undefined, {
401
- blockNumber: BlockNumber(latestBlockNumber + 1),
618
+ blockNumber: BlockNumber(latestHeader.globalVariables.blockNumber + 1),
402
619
  timestamp: this.nextBlockTimestamp,
403
620
  version: this.version,
404
621
  chainId: this.chainId,
405
622
  });
406
623
 
407
624
  this.oracleHandler = new TXEOraclePublicContext(
408
- contractAddress ?? DEFAULT_ADDRESS,
625
+ contractAddress,
409
626
  await this.stateMachine.synchronizer.nativeWorldStateService.fork(),
410
627
  getSingleTxBlockRequestHash(globalVariables.blockNumber),
411
628
  globalVariables,
629
+ this.contractStore,
412
630
  );
413
631
 
414
632
  this.state = { name: 'PUBLIC' };
415
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);
416
637
  }
417
638
 
418
- async enterUtilityState(contractAddress: AztecAddress = DEFAULT_ADDRESS) {
639
+ async enterUtilityState(contractAddressOpt: Option<AztecAddress>) {
640
+ const contractAddress = contractAddressOpt?.value ?? DEFAULT_ADDRESS;
419
641
  this.exitTopLevelState();
642
+ this.resetLastCall();
420
643
 
421
644
  const anchorBlockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
422
645
 
@@ -430,7 +653,7 @@ export class TXESession implements TXESessionStateHandler {
430
653
  this.stateMachine.node,
431
654
  anchorBlockHeader,
432
655
  this.currentJobId,
433
- ).syncNoteNullifiers(contractAddress, 'ALL_SCOPES');
656
+ ).syncNoteNullifiers(contractAddress, await this.keyStore.getAccounts());
434
657
 
435
658
  this.oracleHandler = new UtilityExecutionOracle({
436
659
  contractAddress,
@@ -444,16 +667,22 @@ export class TXESession implements TXESessionStateHandler {
444
667
  aztecNode: this.stateMachine.node,
445
668
  recipientTaggingStore: this.recipientTaggingStore,
446
669
  senderAddressBookStore: this.senderAddressBookStore,
447
- capsuleStore: this.capsuleStore,
670
+ capsuleService: new CapsuleService(this.capsuleStore, await this.keyStore.getAccounts()),
448
671
  privateEventStore: this.privateEventStore,
449
672
  messageContextService: this.stateMachine.messageContextService,
450
- contractSyncService: this.contractSyncService,
673
+ contractSyncService: this.stateMachine.contractSyncService,
674
+ l2TipsStore: this.stateMachine.l2TipsProvider,
451
675
  jobId: this.currentJobId,
452
- scopes: 'ALL_SCOPES',
676
+ scopes: await this.keyStore.getAccounts(),
677
+ simulator: new WASMSimulator(),
678
+ utilityExecutor: this.utilityExecutorForContractSync(anchorBlockHeader),
453
679
  });
454
680
 
455
681
  this.state = { name: 'UTILITY' };
456
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);
457
686
  }
458
687
 
459
688
  private exitTopLevelState() {
@@ -518,13 +747,14 @@ export class TXESession implements TXESessionStateHandler {
518
747
  }
519
748
 
520
749
  private utilityExecutorForContractSync(anchorBlock: any) {
521
- return async (call: FunctionCall, scopes: AccessScopes) => {
750
+ return async (call: FunctionCall, scopes: AztecAddress[]) => {
522
751
  const entryPointArtifact = await this.contractStore.getFunctionArtifactWithDebugMetadata(call.to, call.selector);
523
752
  if (entryPointArtifact.functionType !== FunctionType.UTILITY) {
524
753
  throw new Error(`Cannot run ${entryPointArtifact.functionType} function as utility`);
525
754
  }
526
755
 
527
756
  try {
757
+ const simulator = new WASMSimulator();
528
758
  const oracle = new UtilityExecutionOracle({
529
759
  contractAddress: call.to,
530
760
  authWitnesses: [],
@@ -537,14 +767,17 @@ export class TXESession implements TXESessionStateHandler {
537
767
  aztecNode: this.stateMachine.node,
538
768
  recipientTaggingStore: this.recipientTaggingStore,
539
769
  senderAddressBookStore: this.senderAddressBookStore,
540
- capsuleStore: this.capsuleStore,
770
+ capsuleService: new CapsuleService(this.capsuleStore, scopes),
541
771
  privateEventStore: this.privateEventStore,
542
772
  messageContextService: this.stateMachine.messageContextService,
543
- contractSyncService: this.contractSyncService,
773
+ contractSyncService: this.stateMachine.contractSyncService,
774
+ l2TipsStore: this.stateMachine.l2TipsProvider,
544
775
  jobId: this.currentJobId,
545
776
  scopes,
777
+ simulator,
778
+ utilityExecutor: this.utilityExecutorForContractSync(anchorBlock),
546
779
  });
547
- await new WASMSimulator()
780
+ await simulator
548
781
  .executeUserCircuit(toACVMWitness(0, call.args), entryPointArtifact, new Oracle(oracle).toACIRCallback())
549
782
  .catch((err: Error) => {
550
783
  err.message = resolveAssertionMessageFromError(err, entryPointArtifact);
@@ -3,6 +3,7 @@ import type { EthAddress } from '@aztec/foundation/eth-address';
3
3
  import { hexToBuffer } from '@aztec/foundation/string';
4
4
  import { type ContractArtifact, ContractArtifactSchema } from '@aztec/stdlib/abi';
5
5
  import { AztecAddress } from '@aztec/stdlib/aztec-address';
6
+ import { BlockHash } from '@aztec/stdlib/block';
6
7
  import { type ContractInstanceWithAddress, ContractInstanceWithAddressSchema } from '@aztec/stdlib/contract';
7
8
 
8
9
  import { z } from 'zod';
@@ -25,6 +26,10 @@ export function addressFromSingle(obj: ForeignCallSingle) {
25
26
  return new AztecAddress(fromSingle(obj));
26
27
  }
27
28
 
29
+ export function blockHashFromSingle(obj: ForeignCallSingle) {
30
+ return new BlockHash(fromSingle(obj));
31
+ }
32
+
28
33
  export function fromArray(obj: ForeignCallArray) {
29
34
  return obj.map(str => Fr.fromBuffer(hexToBuffer(str)));
30
35
  }
@@ -142,9 +147,10 @@ export function arrayOfArraysToBoundedVecOfArrays(
142
147
 
143
148
  const numFieldsToPad = maxLen * nestedArrayLength - flattenedStorage.length;
144
149
 
145
- 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))));
146
153
 
147
- // At last we get the actual length of the BoundedVec and return the values.
148
154
  const len = toSingle(new Fr(bVecStorage.length));
149
155
  return [flattenedStorageWithPadding, len];
150
156
  }