@aztec/txe 0.0.1-commit.181e2d196 → 0.0.1-commit.189eedb3

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-6DNQGQKE.js +265 -0
  20. package/dest/chunk-6DNQGQKE.js.map +7 -0
  21. package/dest/chunk-BJVAAXNA.js +3 -0
  22. package/dest/chunk-BJVAAXNA.js.map +7 -0
  23. package/dest/chunk-IGFYWSUP.js +304 -0
  24. package/dest/chunk-IGFYWSUP.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 -188
  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 +55 -30
  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 +38 -14
  50. package/dest/oracle/txe_oracle_public_context.d.ts.map +1 -1
  51. package/dest/oracle/txe_oracle_public_context.js +55 -13
  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 +43 -23
  56. package/dest/oracle/txe_oracle_top_level_context.d.ts.map +1 -1
  57. package/dest/oracle/txe_oracle_top_level_context.js +163 -69
  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 -228
  68. package/dest/rpc_translator.d.ts.map +1 -1
  69. package/dest/rpc_translator.js +815 -636
  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 +6 -5
  73. package/dest/state_machine/archiver.d.ts.map +1 -1
  74. package/dest/state_machine/archiver.js +30 -21
  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 +7 -3
  82. package/dest/state_machine/index.d.ts.map +1 -1
  83. package/dest/state_machine/index.js +23 -5
  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 +175 -37
  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 -224
  118. package/src/msgpackr_fr_extension.ts +23 -0
  119. package/src/oracle/interfaces.ts +53 -35
  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 +72 -12
  124. package/src/oracle/txe_oracle_registry.ts +401 -0
  125. package/src/oracle/txe_oracle_top_level_context.ts +197 -79
  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 +1000 -1021
  130. package/src/state_machine/archiver.ts +42 -20
  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 +25 -6
  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 +286 -40
  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.txeAdvanceBlocksBy(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,11 +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(),
590
+ messageContextService: this.stateMachine.messageContextService,
591
+ simulator: new WASMSimulator(),
372
592
  });
373
593
 
374
594
  // We store the note and tagging index caches fed into the PrivateExecutionOracle (along with some other auxiliary
@@ -379,35 +599,47 @@ export class TXESession implements TXESessionStateHandler {
379
599
  this.state = { name: 'PRIVATE', nextBlockGlobalVariables, noteCache, taggingIndexCache };
380
600
  this.logger.debug(`Entered state ${this.state.name}`);
381
601
 
382
- 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();
383
607
  }
384
608
 
385
- async enterPublicState(contractAddress?: AztecAddress) {
609
+ async enterPublicState(contractAddressOpt: Option<AztecAddress>) {
610
+ const contractAddress = contractAddressOpt?.value ?? DEFAULT_ADDRESS;
386
611
  this.exitTopLevelState();
612
+ this.resetLastCall();
387
613
 
388
614
  // The PublicContext will create a block with a single transaction in it, containing the effects of what was done in
389
615
  // the test. The block therefore gets the *next* block number and timestamp.
390
- const latestBlockNumber = (await this.stateMachine.node.getBlockHeader('latest'))!.globalVariables.blockNumber;
616
+ const latestHeader = (await this.stateMachine.node.getBlockData('latest'))!.header;
391
617
  const globalVariables = makeGlobalVariables(undefined, {
392
- blockNumber: BlockNumber(latestBlockNumber + 1),
618
+ blockNumber: BlockNumber(latestHeader.globalVariables.blockNumber + 1),
393
619
  timestamp: this.nextBlockTimestamp,
394
620
  version: this.version,
395
621
  chainId: this.chainId,
396
622
  });
397
623
 
398
624
  this.oracleHandler = new TXEOraclePublicContext(
399
- contractAddress ?? DEFAULT_ADDRESS,
625
+ contractAddress,
400
626
  await this.stateMachine.synchronizer.nativeWorldStateService.fork(),
401
627
  getSingleTxBlockRequestHash(globalVariables.blockNumber),
402
628
  globalVariables,
629
+ this.contractStore,
403
630
  );
404
631
 
405
632
  this.state = { name: 'PUBLIC' };
406
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);
407
637
  }
408
638
 
409
- async enterUtilityState(contractAddress: AztecAddress = DEFAULT_ADDRESS) {
639
+ async enterUtilityState(contractAddressOpt: Option<AztecAddress>) {
640
+ const contractAddress = contractAddressOpt?.value ?? DEFAULT_ADDRESS;
410
641
  this.exitTopLevelState();
642
+ this.resetLastCall();
411
643
 
412
644
  const anchorBlockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
413
645
 
@@ -421,7 +653,7 @@ export class TXESession implements TXESessionStateHandler {
421
653
  this.stateMachine.node,
422
654
  anchorBlockHeader,
423
655
  this.currentJobId,
424
- ).syncNoteNullifiers(contractAddress, 'ALL_SCOPES');
656
+ ).syncNoteNullifiers(contractAddress, await this.keyStore.getAccounts());
425
657
 
426
658
  this.oracleHandler = new UtilityExecutionOracle({
427
659
  contractAddress,
@@ -435,14 +667,22 @@ export class TXESession implements TXESessionStateHandler {
435
667
  aztecNode: this.stateMachine.node,
436
668
  recipientTaggingStore: this.recipientTaggingStore,
437
669
  senderAddressBookStore: this.senderAddressBookStore,
438
- capsuleStore: this.capsuleStore,
670
+ capsuleService: new CapsuleService(this.capsuleStore, await this.keyStore.getAccounts()),
439
671
  privateEventStore: this.privateEventStore,
672
+ messageContextService: this.stateMachine.messageContextService,
673
+ contractSyncService: this.stateMachine.contractSyncService,
674
+ l2TipsStore: this.stateMachine.l2TipsProvider,
440
675
  jobId: this.currentJobId,
441
- scopes: 'ALL_SCOPES',
676
+ scopes: await this.keyStore.getAccounts(),
677
+ simulator: new WASMSimulator(),
678
+ utilityExecutor: this.utilityExecutorForContractSync(anchorBlockHeader),
442
679
  });
443
680
 
444
681
  this.state = { name: 'UTILITY' };
445
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);
446
686
  }
447
687
 
448
688
  private exitTopLevelState() {
@@ -452,8 +692,8 @@ export class TXESession implements TXESessionStateHandler {
452
692
 
453
693
  // Note that while all public and private contexts do is build a single block that we then process when exiting
454
694
  // those, the top level context performs a large number of actions not captured in the following 'close' call. Among
455
- // others, it will create empty blocks (via `txeAdvanceBlocksBy` and `deploy`), create blocks with transactions via
456
- // `txePrivateCallNewFlow` and `txePublicCallNewFlow`, add accounts to PXE via `txeAddAccount`, etc. This is a
695
+ // others, it will create empty blocks (via `advanceBlocksBy` and `deploy`), create blocks with transactions via
696
+ // `privateCallNewFlow` and `publicCallNewFlow`, add accounts to PXE via `addAccount`, etc. This is a
457
697
  // slight inconsistency in the working model of this class, but is not too bad.
458
698
  // TODO: it's quite unfortunate that we need to capture the authwits created to later pass them again when the top
459
699
  // level context is re-created. This is because authwits create a temporary utility context that'd otherwise reset
@@ -507,13 +747,14 @@ export class TXESession implements TXESessionStateHandler {
507
747
  }
508
748
 
509
749
  private utilityExecutorForContractSync(anchorBlock: any) {
510
- return async (call: FunctionCall, scopes: AccessScopes) => {
750
+ return async (call: FunctionCall, scopes: AztecAddress[]) => {
511
751
  const entryPointArtifact = await this.contractStore.getFunctionArtifactWithDebugMetadata(call.to, call.selector);
512
752
  if (entryPointArtifact.functionType !== FunctionType.UTILITY) {
513
753
  throw new Error(`Cannot run ${entryPointArtifact.functionType} function as utility`);
514
754
  }
515
755
 
516
756
  try {
757
+ const simulator = new WASMSimulator();
517
758
  const oracle = new UtilityExecutionOracle({
518
759
  contractAddress: call.to,
519
760
  authWitnesses: [],
@@ -526,12 +767,17 @@ export class TXESession implements TXESessionStateHandler {
526
767
  aztecNode: this.stateMachine.node,
527
768
  recipientTaggingStore: this.recipientTaggingStore,
528
769
  senderAddressBookStore: this.senderAddressBookStore,
529
- capsuleStore: this.capsuleStore,
770
+ capsuleService: new CapsuleService(this.capsuleStore, scopes),
530
771
  privateEventStore: this.privateEventStore,
772
+ messageContextService: this.stateMachine.messageContextService,
773
+ contractSyncService: this.stateMachine.contractSyncService,
774
+ l2TipsStore: this.stateMachine.l2TipsProvider,
531
775
  jobId: this.currentJobId,
532
776
  scopes,
777
+ simulator,
778
+ utilityExecutor: this.utilityExecutorForContractSync(anchorBlock),
533
779
  });
534
- await new WASMSimulator()
780
+ await simulator
535
781
  .executeUserCircuit(toACVMWitness(0, call.args), entryPointArtifact, new Oracle(oracle).toACIRCallback())
536
782
  .catch((err: Error) => {
537
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
  }