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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (150) hide show
  1. package/dest/ContractClassRegistry-PII7Q556.js +3 -0
  2. package/dest/ContractClassRegistry-PII7Q556.js.map +7 -0
  3. package/dest/ContractInstanceRegistry-3MHLGFNZ.js +3 -0
  4. package/dest/ContractInstanceRegistry-3MHLGFNZ.js.map +7 -0
  5. package/dest/FeeJuice-TQV5AQK7.js +3 -0
  6. package/dest/FeeJuice-TQV5AQK7.js.map +7 -0
  7. package/dest/MultiCallEntrypoint-SBJUIJY7.js +3 -0
  8. package/dest/MultiCallEntrypoint-SBJUIJY7.js.map +7 -0
  9. package/dest/SchnorrAccount-MQXPIZ44.js +3 -0
  10. package/dest/SchnorrAccount-MQXPIZ44.js.map +7 -0
  11. package/dest/bin/check_txe_oracle_version.d.ts +2 -0
  12. package/dest/bin/check_txe_oracle_version.d.ts.map +1 -0
  13. package/dest/bin/check_txe_oracle_version.js +61 -0
  14. package/dest/bin/index.js +3 -30
  15. package/dest/bin/index.js.map +7 -0
  16. package/dest/bin/oracle_test_server.d.ts +3 -0
  17. package/dest/bin/oracle_test_server.d.ts.map +1 -0
  18. package/dest/bin/oracle_test_server.js +41 -0
  19. package/dest/chunk-2FGZE6YL.js +265 -0
  20. package/dest/chunk-2FGZE6YL.js.map +7 -0
  21. package/dest/chunk-6HRVNWJU.js +304 -0
  22. package/dest/chunk-6HRVNWJU.js.map +7 -0
  23. package/dest/chunk-BJVAAXNA.js +3 -0
  24. package/dest/chunk-BJVAAXNA.js.map +7 -0
  25. package/dest/constants.d.ts +5 -1
  26. package/dest/constants.d.ts.map +1 -1
  27. package/dest/constants.js +8 -0
  28. package/dest/dispatcher_pool.d.ts +67 -0
  29. package/dest/dispatcher_pool.d.ts.map +1 -0
  30. package/dest/dispatcher_pool.js +284 -0
  31. package/dest/index.d.ts +185 -7
  32. package/dest/index.d.ts.map +1 -1
  33. package/dest/index.js +70 -190
  34. package/dest/metafile.json +38642 -0
  35. package/dest/msgpackr_fr_extension.d.ts +2 -0
  36. package/dest/msgpackr_fr_extension.d.ts.map +1 -0
  37. package/dest/msgpackr_fr_extension.js +21 -0
  38. package/dest/oracle/interfaces.d.ts +33 -8
  39. package/dest/oracle/interfaces.d.ts.map +1 -1
  40. package/dest/oracle/test-resolver/fixtures.d.ts +43 -0
  41. package/dest/oracle/test-resolver/fixtures.d.ts.map +1 -0
  42. package/dest/oracle/test-resolver/fixtures.js +39 -0
  43. package/dest/oracle/test-resolver/index.d.ts +9 -0
  44. package/dest/oracle/test-resolver/index.d.ts.map +1 -0
  45. package/dest/oracle/test-resolver/index.js +33 -0
  46. package/dest/oracle/test-resolver/resolver.d.ts +34 -0
  47. package/dest/oracle/test-resolver/resolver.d.ts.map +1 -0
  48. package/dest/oracle/test-resolver/resolver.js +114 -0
  49. package/dest/oracle/txe_oracle_public_context.d.ts +26 -2
  50. package/dest/oracle/txe_oracle_public_context.d.ts.map +1 -1
  51. package/dest/oracle/txe_oracle_public_context.js +43 -1
  52. package/dest/oracle/txe_oracle_registry.d.ts +147 -0
  53. package/dest/oracle/txe_oracle_registry.d.ts.map +1 -0
  54. package/dest/oracle/txe_oracle_registry.js +562 -0
  55. package/dest/oracle/txe_oracle_top_level_context.d.ts +32 -12
  56. package/dest/oracle/txe_oracle_top_level_context.d.ts.map +1 -1
  57. package/dest/oracle/txe_oracle_top_level_context.js +146 -54
  58. package/dest/oracle/txe_oracle_version.d.ts +17 -0
  59. package/dest/oracle/txe_oracle_version.d.ts.map +1 -0
  60. package/dest/oracle/txe_oracle_version.js +14 -0
  61. package/dest/oracle/txe_private_execution_oracle.d.ts +17 -0
  62. package/dest/oracle/txe_private_execution_oracle.d.ts.map +1 -0
  63. package/dest/oracle/txe_private_execution_oracle.js +15 -0
  64. package/dest/rpc_server.d.ts +14 -0
  65. package/dest/rpc_server.d.ts.map +1 -0
  66. package/dest/rpc_server.js +78 -0
  67. package/dest/rpc_translator.d.ts +96 -230
  68. package/dest/rpc_translator.d.ts.map +1 -1
  69. package/dest/rpc_translator.js +708 -616
  70. package/dest/server.bundle.js +3 -0
  71. package/dest/server.bundle.js.map +7 -0
  72. package/dest/state_machine/archiver.d.ts +4 -3
  73. package/dest/state_machine/archiver.d.ts.map +1 -1
  74. package/dest/state_machine/archiver.js +26 -15
  75. package/dest/state_machine/dummy_p2p_client.d.ts +13 -7
  76. package/dest/state_machine/dummy_p2p_client.d.ts.map +1 -1
  77. package/dest/state_machine/dummy_p2p_client.js +16 -4
  78. package/dest/state_machine/global_variable_builder.d.ts +9 -4
  79. package/dest/state_machine/global_variable_builder.d.ts.map +1 -1
  80. package/dest/state_machine/global_variable_builder.js +9 -3
  81. package/dest/state_machine/index.d.ts +4 -2
  82. package/dest/state_machine/index.d.ts.map +1 -1
  83. package/dest/state_machine/index.js +17 -3
  84. package/dest/state_machine/mock_epoch_cache.d.ts +16 -3
  85. package/dest/state_machine/mock_epoch_cache.d.ts.map +1 -1
  86. package/dest/state_machine/mock_epoch_cache.js +29 -2
  87. package/dest/state_machine/synchronizer.d.ts +5 -5
  88. package/dest/state_machine/synchronizer.d.ts.map +1 -1
  89. package/dest/state_machine/synchronizer.js +4 -4
  90. package/dest/txe_session.d.ts +74 -12
  91. package/dest/txe_session.d.ts.map +1 -1
  92. package/dest/txe_session.js +171 -36
  93. package/dest/utils/encoding.d.ts +191 -0
  94. package/dest/utils/encoding.d.ts.map +1 -0
  95. package/dest/{util → utils}/encoding.js +7 -2
  96. package/dest/{util → utils}/expected_failure_error.d.ts +1 -1
  97. package/dest/utils/expected_failure_error.d.ts.map +1 -0
  98. package/dest/{util → utils}/txe_account_store.d.ts +1 -1
  99. package/dest/utils/txe_account_store.d.ts.map +1 -0
  100. package/dest/utils/txe_artifact_resolver.d.ts +37 -0
  101. package/dest/utils/txe_artifact_resolver.d.ts.map +1 -0
  102. package/dest/utils/txe_artifact_resolver.js +161 -0
  103. package/dest/utils/txe_public_contract_data_source.d.ts +20 -0
  104. package/dest/utils/txe_public_contract_data_source.d.ts.map +1 -0
  105. package/dest/{util → utils}/txe_public_contract_data_source.js +1 -3
  106. package/dest/worker.bundle.js +3 -0
  107. package/dest/worker.bundle.js.map +7 -0
  108. package/dest/worker.d.ts +2 -0
  109. package/dest/worker.d.ts.map +1 -0
  110. package/dest/worker.js +92 -0
  111. package/package.json +38 -21
  112. package/src/bin/check_txe_oracle_version.ts +70 -0
  113. package/src/bin/index.ts +11 -2
  114. package/src/bin/oracle_test_server.ts +51 -0
  115. package/src/constants.ts +10 -0
  116. package/src/dispatcher_pool.ts +316 -0
  117. package/src/index.ts +97 -227
  118. package/src/msgpackr_fr_extension.ts +23 -0
  119. package/src/oracle/interfaces.ts +29 -7
  120. package/src/oracle/test-resolver/fixtures.ts +84 -0
  121. package/src/oracle/test-resolver/index.ts +45 -0
  122. package/src/oracle/test-resolver/resolver.ts +165 -0
  123. package/src/oracle/txe_oracle_public_context.ts +60 -0
  124. package/src/oracle/txe_oracle_registry.ts +401 -0
  125. package/src/oracle/txe_oracle_top_level_context.ts +177 -61
  126. package/src/oracle/txe_oracle_version.ts +17 -0
  127. package/src/oracle/txe_private_execution_oracle.ts +30 -0
  128. package/src/rpc_server.ts +87 -0
  129. package/src/rpc_translator.ts +789 -903
  130. package/src/state_machine/archiver.ts +38 -16
  131. package/src/state_machine/dummy_p2p_client.ts +31 -11
  132. package/src/state_machine/global_variable_builder.ts +18 -3
  133. package/src/state_machine/index.ts +20 -5
  134. package/src/state_machine/mock_epoch_cache.ts +38 -3
  135. package/src/state_machine/synchronizer.ts +5 -5
  136. package/src/txe_session.ts +281 -38
  137. package/src/{util → utils}/encoding.ts +8 -2
  138. package/src/utils/txe_artifact_resolver.ts +217 -0
  139. package/src/{util → utils}/txe_public_contract_data_source.ts +0 -2
  140. package/src/worker.ts +98 -0
  141. package/dest/util/encoding.d.ts +0 -720
  142. package/dest/util/encoding.d.ts.map +0 -1
  143. package/dest/util/expected_failure_error.d.ts.map +0 -1
  144. package/dest/util/txe_account_store.d.ts.map +0 -1
  145. package/dest/util/txe_public_contract_data_source.d.ts +0 -20
  146. package/dest/util/txe_public_contract_data_source.d.ts.map +0 -1
  147. /package/dest/{util → utils}/expected_failure_error.js +0 -0
  148. /package/dest/{util → utils}/txe_account_store.js +0 -0
  149. /package/src/{util → utils}/expected_failure_error.ts +0 -0
  150. /package/src/{util → utils}/txe_account_store.ts +0 -0
@@ -2,11 +2,12 @@ import { BlockNumber } from '@aztec/foundation/branded-types';
2
2
  import { Fr } from '@aztec/foundation/curves/bn254';
3
3
  import { type Logger, createLogger } from '@aztec/foundation/log';
4
4
  import { KeyStore } from '@aztec/key-store';
5
- import { openTmpStore } from '@aztec/kv-store/lmdb-v2';
6
- import type { AccessScopes } from '@aztec/pxe/client/lazy';
5
+ import type { AztecAsyncKVStore } from '@aztec/kv-store';
6
+ import { openEphemeralStore } from '@aztec/kv-store/lmdb-v2';
7
7
  import {
8
8
  AddressStore,
9
9
  AnchorBlockStore,
10
+ CapsuleService,
10
11
  CapsuleStore,
11
12
  ContractStore,
12
13
  JobCoordinator,
@@ -21,10 +22,11 @@ import {
21
22
  ExecutionNoteCache,
22
23
  ExecutionTaggingIndexCache,
23
24
  HashedValuesCache,
25
+ type IMiscOracle,
24
26
  type IPrivateExecutionOracle,
25
27
  type IUtilityExecutionOracle,
28
+ Option,
26
29
  Oracle,
27
- PrivateExecutionOracle,
28
30
  UtilityExecutionOracle,
29
31
  } from '@aztec/pxe/simulator';
30
32
  import {
@@ -35,28 +37,32 @@ import {
35
37
  resolveAssertionMessageFromError,
36
38
  toACVMWitness,
37
39
  } from '@aztec/simulator/client';
40
+ import { STANDARD_AUTH_REGISTRY_ADDRESS } from '@aztec/standard-contracts/auth-registry/constants';
38
41
  import { FunctionCall, FunctionSelector, FunctionType } from '@aztec/stdlib/abi';
39
42
  import type { AuthWitness } from '@aztec/stdlib/auth-witness';
40
43
  import { AztecAddress } from '@aztec/stdlib/aztec-address';
41
- import { GasSettings } from '@aztec/stdlib/gas';
44
+ import type { GasSettings } from '@aztec/stdlib/gas';
42
45
  import { computeProtocolNullifier } from '@aztec/stdlib/hash';
43
46
  import { PrivateContextInputs } from '@aztec/stdlib/kernel';
44
47
  import { makeGlobalVariables } from '@aztec/stdlib/testing';
45
- import { CallContext, GlobalVariables, TxContext } from '@aztec/stdlib/tx';
48
+ import { CallContext, GlobalVariables, OFFCHAIN_MESSAGE_IDENTIFIER, TxContext } from '@aztec/stdlib/tx';
46
49
 
47
50
  import { z } from 'zod';
48
51
 
49
- import { DEFAULT_ADDRESS } from './constants.js';
52
+ import { DEFAULT_ADDRESS, MAX_OFFCHAIN_EFFECTS_PER_TXE_QUERY, MAX_OFFCHAIN_EFFECT_LEN } from './constants.js';
50
53
  import type { IAvmExecutionOracle, ITxeExecutionOracle } from './oracle/interfaces.js';
51
54
  import { TXEOraclePublicContext } from './oracle/txe_oracle_public_context.js';
52
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';
53
58
  import { RPCTranslator } from './rpc_translator.js';
54
59
  import { TXEArchiver } from './state_machine/archiver.js';
55
60
  import { TXEStateMachine } from './state_machine/index.js';
56
- import type { ForeignCallArgs, ForeignCallResult } from './util/encoding.js';
57
- import { TXEAccountStore } from './util/txe_account_store.js';
58
61
  import { getSingleTxBlockRequestHash, insertTxEffectIntoWorldTrees, makeTXEBlock } from './utils/block_creation.js';
62
+ import type { ForeignCallArgs, ForeignCallResult } from './utils/encoding.js';
59
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';
60
66
 
61
67
  /**
62
68
  * A TXE Session can be in one of four states, which change as the test progresses and different oracles are called.
@@ -109,14 +115,80 @@ export type TXEOracleFunctionName = Exclude<
109
115
  >;
110
116
 
111
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
+
112
121
  enterTopLevelState(): Promise<void>;
113
- enterPublicState(contractAddress?: AztecAddress): Promise<void>;
114
- enterPrivateState(contractAddress?: AztecAddress, anchorBlockNumber?: BlockNumber): Promise<PrivateContextInputs>;
115
- 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>;
116
129
 
117
130
  // TODO(F-335): Exposing the job info is abstraction breakage - drop the following 2 functions.
118
131
  cycleJob(): Promise<string>;
119
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 };
120
192
  }
121
193
 
122
194
  /**
@@ -126,11 +198,17 @@ export interface TXESessionStateHandler {
126
198
  export class TXESession implements TXESessionStateHandler {
127
199
  private state: SessionState = { name: 'TOP_LEVEL' };
128
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;
129
205
 
130
206
  constructor(
131
207
  private logger: Logger,
208
+ private sessionStore: AztecAsyncKVStore,
132
209
  private stateMachine: TXEStateMachine,
133
210
  private oracleHandler:
211
+ | IMiscOracle
134
212
  | IUtilityExecutionOracle
135
213
  | IPrivateExecutionOracle
136
214
  | IAvmExecutionOracle
@@ -150,10 +228,43 @@ export class TXESession implements TXESessionStateHandler {
150
228
  private chainId: Fr,
151
229
  private version: Fr,
152
230
  private nextBlockTimestamp: bigint,
231
+ private readonly artifactResolver: TXEArtifactResolver,
232
+ private readonly rootPath: string,
233
+ private readonly packageName: string,
153
234
  ) {}
154
235
 
155
- static async init(contractStore: ContractStore) {
156
- 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);
157
268
 
158
269
  const addressStore = new AddressStore(store);
159
270
  const privateEventStore = new PrivateEventStore(store);
@@ -165,7 +276,6 @@ export class TXESession implements TXESessionStateHandler {
165
276
  const keyStore = new KeyStore(store);
166
277
  const accountStore = new TXEAccountStore(store);
167
278
 
168
- // Create job coordinator and register staged stores
169
279
  const jobCoordinator = new JobCoordinator(store);
170
280
  jobCoordinator.registerStores([
171
281
  capsuleStore,
@@ -185,6 +295,8 @@ export class TXESession implements TXESessionStateHandler {
185
295
 
186
296
  const initialJobId = jobCoordinator.beginJob();
187
297
 
298
+ const logger = createLogger('txe:session');
299
+
188
300
  const topLevelOracleHandler = new TXEOracleTopLevelContext(
189
301
  stateMachine,
190
302
  contractStore,
@@ -201,11 +313,16 @@ export class TXESession implements TXESessionStateHandler {
201
313
  version,
202
314
  chainId,
203
315
  new Map(),
316
+ artifactResolver,
317
+ rootPath,
318
+ packageName,
204
319
  );
205
- await topLevelOracleHandler.advanceBlocksBy(1);
320
+
321
+ await topLevelOracleHandler.mineDeploymentNullifiers([STANDARD_AUTH_REGISTRY_ADDRESS]);
206
322
 
207
323
  return new TXESession(
208
- createLogger('txe:session'),
324
+ logger,
325
+ store,
209
326
  stateMachine,
210
327
  topLevelOracleHandler,
211
328
  contractStore,
@@ -223,6 +340,9 @@ export class TXESession implements TXESessionStateHandler {
223
340
  version,
224
341
  chainId,
225
342
  nextBlockTimestamp,
343
+ artifactResolver,
344
+ rootPath,
345
+ packageName,
226
346
  );
227
347
  }
228
348
 
@@ -244,7 +364,28 @@ export class TXESession implements TXESessionStateHandler {
244
364
  return translator[validatedFunctionName](...inputs);
245
365
  } catch (error) {
246
366
  if (error instanceof z.ZodError) {
247
- 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}`);
248
389
  } else if (error instanceof Error) {
249
390
  throw new Error(
250
391
  `Execution error while processing function ${functionName} in state ${this.state.name}: ${error.message}`,
@@ -268,6 +409,75 @@ export class TXESession implements TXESessionStateHandler {
268
409
  return this.currentJobId;
269
410
  }
270
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
+
271
481
  async enterTopLevelState() {
272
482
  switch (this.state.name) {
273
483
  case 'PRIVATE': {
@@ -309,6 +519,9 @@ export class TXESession implements TXESessionStateHandler {
309
519
  this.version,
310
520
  this.chainId,
311
521
  this.authwits,
522
+ this.artifactResolver,
523
+ this.rootPath,
524
+ this.packageName,
312
525
  );
313
526
 
314
527
  this.state = { name: 'TOP_LEVEL' };
@@ -316,21 +529,25 @@ export class TXESession implements TXESessionStateHandler {
316
529
  }
317
530
 
318
531
  async enterPrivateState(
319
- contractAddress: AztecAddress = DEFAULT_ADDRESS,
320
- anchorBlockNumber?: BlockNumber,
532
+ contractAddressOpt: Option<AztecAddress>,
533
+ anchorBlockNumberOpt: Option<BlockNumber>,
534
+ gasSettings: GasSettings,
321
535
  ): Promise<PrivateContextInputs> {
536
+ const contractAddress = contractAddressOpt?.value ?? DEFAULT_ADDRESS;
537
+ const anchorBlockNumber = anchorBlockNumberOpt?.value;
322
538
  this.exitTopLevelState();
539
+ this.resetLastCall();
323
540
 
324
541
  // Private execution has two associated block numbers: the anchor block (i.e. the historical block that is used to
325
542
  // build the proof), and the *next* block, i.e. the one we'll create once the execution ends, and which will contain
326
543
  // a single transaction with the effects of what was done in the test.
327
- const anchorBlock = await this.stateMachine.node.getBlockHeader(anchorBlockNumber ?? 'latest');
544
+ const anchorBlock = await this.stateMachine.node.getBlock(anchorBlockNumber ?? 'latest').then(b => b?.header);
328
545
 
329
546
  await new NoteService(this.noteStore, this.stateMachine.node, anchorBlock!, this.currentJobId).syncNoteNullifiers(
330
547
  contractAddress,
331
- 'ALL_SCOPES',
548
+ await this.keyStore.getAccounts(),
332
549
  );
333
- const latestBlock = await this.stateMachine.node.getBlockHeader('latest');
550
+ const latestBlock = await this.stateMachine.node.getBlock('latest').then(b => b?.header);
334
551
 
335
552
  const nextBlockGlobalVariables = makeGlobalVariables(undefined, {
336
553
  blockNumber: BlockNumber(latestBlock!.globalVariables.blockNumber + 1),
@@ -345,9 +562,9 @@ export class TXESession implements TXESessionStateHandler {
345
562
  const taggingIndexCache = new ExecutionTaggingIndexCache();
346
563
 
347
564
  const utilityExecutor = this.utilityExecutorForContractSync(anchorBlock);
348
- this.oracleHandler = new PrivateExecutionOracle({
565
+ this.oracleHandler = new TXEPrivateExecutionOracle({
349
566
  argsHash: Fr.ZERO,
350
- txContext: new TxContext(this.chainId, this.version, GasSettings.empty()),
567
+ txContext: new TxContext(this.chainId, this.version, gasSettings),
351
568
  callContext: new CallContext(AztecAddress.ZERO, contractAddress, FunctionSelector.empty(), false),
352
569
  anchorBlockHeader: anchorBlock!,
353
570
  utilityExecutor,
@@ -364,12 +581,14 @@ export class TXESession implements TXESessionStateHandler {
364
581
  senderTaggingStore: this.senderTaggingStore,
365
582
  recipientTaggingStore: this.recipientTaggingStore,
366
583
  senderAddressBookStore: this.senderAddressBookStore,
367
- capsuleStore: this.capsuleStore,
584
+ capsuleService: new CapsuleService(this.capsuleStore, await this.keyStore.getAccounts()),
368
585
  privateEventStore: this.privateEventStore,
369
586
  contractSyncService: this.stateMachine.contractSyncService,
587
+ l2TipsStore: this.stateMachine.l2TipsProvider,
370
588
  jobId: this.currentJobId,
371
- scopes: 'ALL_SCOPES',
589
+ scopes: await this.keyStore.getAccounts(),
372
590
  messageContextService: this.stateMachine.messageContextService,
591
+ simulator: new WASMSimulator(),
373
592
  });
374
593
 
375
594
  // We store the note and tagging index caches fed into the PrivateExecutionOracle (along with some other auxiliary
@@ -380,35 +599,47 @@ export class TXESession implements TXESessionStateHandler {
380
599
  this.state = { name: 'PRIVATE', nextBlockGlobalVariables, noteCache, taggingIndexCache };
381
600
  this.logger.debug(`Entered state ${this.state.name}`);
382
601
 
383
- 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();
384
607
  }
385
608
 
386
- async enterPublicState(contractAddress?: AztecAddress) {
609
+ async enterPublicState(contractAddressOpt: Option<AztecAddress>) {
610
+ const contractAddress = contractAddressOpt?.value ?? DEFAULT_ADDRESS;
387
611
  this.exitTopLevelState();
612
+ this.resetLastCall();
388
613
 
389
614
  // The PublicContext will create a block with a single transaction in it, containing the effects of what was done in
390
615
  // the test. The block therefore gets the *next* block number and timestamp.
391
- const latestBlockNumber = (await this.stateMachine.node.getBlockHeader('latest'))!.globalVariables.blockNumber;
616
+ const latestHeader = (await this.stateMachine.node.getBlockData('latest'))!.header;
392
617
  const globalVariables = makeGlobalVariables(undefined, {
393
- blockNumber: BlockNumber(latestBlockNumber + 1),
618
+ blockNumber: BlockNumber(latestHeader.globalVariables.blockNumber + 1),
394
619
  timestamp: this.nextBlockTimestamp,
395
620
  version: this.version,
396
621
  chainId: this.chainId,
397
622
  });
398
623
 
399
624
  this.oracleHandler = new TXEOraclePublicContext(
400
- contractAddress ?? DEFAULT_ADDRESS,
625
+ contractAddress,
401
626
  await this.stateMachine.synchronizer.nativeWorldStateService.fork(),
402
627
  getSingleTxBlockRequestHash(globalVariables.blockNumber),
403
628
  globalVariables,
629
+ this.contractStore,
404
630
  );
405
631
 
406
632
  this.state = { name: 'PUBLIC' };
407
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);
408
637
  }
409
638
 
410
- async enterUtilityState(contractAddress: AztecAddress = DEFAULT_ADDRESS) {
639
+ async enterUtilityState(contractAddressOpt: Option<AztecAddress>) {
640
+ const contractAddress = contractAddressOpt?.value ?? DEFAULT_ADDRESS;
411
641
  this.exitTopLevelState();
642
+ this.resetLastCall();
412
643
 
413
644
  const anchorBlockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
414
645
 
@@ -422,7 +653,7 @@ export class TXESession implements TXESessionStateHandler {
422
653
  this.stateMachine.node,
423
654
  anchorBlockHeader,
424
655
  this.currentJobId,
425
- ).syncNoteNullifiers(contractAddress, 'ALL_SCOPES');
656
+ ).syncNoteNullifiers(contractAddress, await this.keyStore.getAccounts());
426
657
 
427
658
  this.oracleHandler = new UtilityExecutionOracle({
428
659
  contractAddress,
@@ -436,15 +667,22 @@ export class TXESession implements TXESessionStateHandler {
436
667
  aztecNode: this.stateMachine.node,
437
668
  recipientTaggingStore: this.recipientTaggingStore,
438
669
  senderAddressBookStore: this.senderAddressBookStore,
439
- capsuleStore: this.capsuleStore,
670
+ capsuleService: new CapsuleService(this.capsuleStore, await this.keyStore.getAccounts()),
440
671
  privateEventStore: this.privateEventStore,
441
672
  messageContextService: this.stateMachine.messageContextService,
673
+ contractSyncService: this.stateMachine.contractSyncService,
674
+ l2TipsStore: this.stateMachine.l2TipsProvider,
442
675
  jobId: this.currentJobId,
443
- scopes: 'ALL_SCOPES',
676
+ scopes: await this.keyStore.getAccounts(),
677
+ simulator: new WASMSimulator(),
678
+ utilityExecutor: this.utilityExecutorForContractSync(anchorBlockHeader),
444
679
  });
445
680
 
446
681
  this.state = { name: 'UTILITY' };
447
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);
448
686
  }
449
687
 
450
688
  private exitTopLevelState() {
@@ -509,13 +747,14 @@ export class TXESession implements TXESessionStateHandler {
509
747
  }
510
748
 
511
749
  private utilityExecutorForContractSync(anchorBlock: any) {
512
- return async (call: FunctionCall, scopes: AccessScopes) => {
750
+ return async (call: FunctionCall, scopes: AztecAddress[]) => {
513
751
  const entryPointArtifact = await this.contractStore.getFunctionArtifactWithDebugMetadata(call.to, call.selector);
514
752
  if (entryPointArtifact.functionType !== FunctionType.UTILITY) {
515
753
  throw new Error(`Cannot run ${entryPointArtifact.functionType} function as utility`);
516
754
  }
517
755
 
518
756
  try {
757
+ const simulator = new WASMSimulator();
519
758
  const oracle = new UtilityExecutionOracle({
520
759
  contractAddress: call.to,
521
760
  authWitnesses: [],
@@ -528,13 +767,17 @@ export class TXESession implements TXESessionStateHandler {
528
767
  aztecNode: this.stateMachine.node,
529
768
  recipientTaggingStore: this.recipientTaggingStore,
530
769
  senderAddressBookStore: this.senderAddressBookStore,
531
- capsuleStore: this.capsuleStore,
770
+ capsuleService: new CapsuleService(this.capsuleStore, scopes),
532
771
  privateEventStore: this.privateEventStore,
533
772
  messageContextService: this.stateMachine.messageContextService,
773
+ contractSyncService: this.stateMachine.contractSyncService,
774
+ l2TipsStore: this.stateMachine.l2TipsProvider,
534
775
  jobId: this.currentJobId,
535
776
  scopes,
777
+ simulator,
778
+ utilityExecutor: this.utilityExecutorForContractSync(anchorBlock),
536
779
  });
537
- await new WASMSimulator()
780
+ await simulator
538
781
  .executeUserCircuit(toACVMWitness(0, call.args), entryPointArtifact, new Oracle(oracle).toACIRCallback())
539
782
  .catch((err: Error) => {
540
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
  }